gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[gnunet-go] branch master updated: MS2-RC1: Recursive resolution and han


From: gnunet
Subject: [gnunet-go] branch master updated: MS2-RC1: Recursive resolution and handling of GNS-specific RRs.
Date: Wed, 18 Dec 2019 17:24:03 +0100

This is an automated email from the git hooks/post-receive script.

bernd-fix pushed a commit to branch master
in repository gnunet-go.

The following commit(s) were added to refs/heads/master by this push:
     new caf6245  MS2-RC1: Recursive resolution and handling of GNS-specific 
RRs.
caf6245 is described below

commit caf624535c2089e01ecc6d4c74f056472b3e54e0
Author: Bernd Fix <address@hidden>
AuthorDate: Wed Dec 18 17:19:32 2019 +0100

    MS2-RC1: Recursive resolution and handling of GNS-specific RRs.
---
 LICENSE                                 | 849 +++++++++++++++++++++++---------
 README.md                               |   1 +
 src/gnunet/config/config.go             |  13 +-
 src/gnunet/core/peer.go                 |  36 +-
 src/gnunet/crypto/gns.go                |  35 ++
 src/gnunet/crypto/gns_test.go           | 150 ++++++
 src/gnunet/crypto/hash.go               |   3 +
 src/gnunet/crypto/key_derivation.go     |   1 +
 src/gnunet/crypto/key_exchange.go       |   4 +-
 src/gnunet/crypto/signature.go          |   1 +
 src/gnunet/crypto/symmetric.go          |  19 +-
 src/gnunet/enums/dht.go                 |  23 +
 src/gnunet/enums/gns.go                 |   1 +
 src/gnunet/message/message.go           |   8 +
 src/gnunet/message/msg_core.go          |  33 +-
 src/gnunet/message/msg_dht.go           |   2 +
 src/gnunet/message/msg_gns.go           |  16 +-
 src/gnunet/message/msg_namecache.go     |   4 +-
 src/gnunet/message/msg_transport.go     | 207 +++++---
 src/gnunet/service/client.go            |   3 +-
 src/gnunet/service/gns/block.go         | 185 +++++++
 src/gnunet/service/gns/block_handler.go | 398 +++++++++++++++
 src/gnunet/service/gns/box.go           | 151 ++++++
 src/gnunet/service/gns/dns.go           |  61 ++-
 src/gnunet/service/gns/module.go        | 257 ++++------
 src/gnunet/service/gns/service.go       |  12 +-
 src/gnunet/service/service.go           |  13 +-
 src/gnunet/transport/channel.go         |   1 +
 src/gnunet/transport/channel_netw.go    |  39 +-
 src/gnunet/transport/connection.go      |   9 +-
 src/gnunet/transport/session.go         |   3 +-
 src/gnunet/util/address.go              |  55 ++-
 src/gnunet/util/array.go                |  10 +
 src/gnunet/util/format.go               |  13 +-
 src/gnunet/util/id.go                   |   3 +-
 src/gnunet/util/peer_id.go              |   3 +
 src/gnunet/util/rnd.go                  |   8 +
 37 files changed, 2030 insertions(+), 600 deletions(-)

diff --git a/LICENSE b/LICENSE
index 0a4f9a1..6cfcd65 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,235 +1,616 @@
-GNU AFFERO GENERAL PUBLIC LICENSE
-Version 3, 19 November 2007
-
-Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007
+Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
  
-Everyone is permitted to copy and distribute verbatim copies of this license 
document, but changing it is not allowed.
-
-                            Preamble
-
-The GNU Affero General Public License is a free, copyleft license for software 
and other kinds of works, specifically designed to ensure cooperation with the 
community in the case of network server software.
-
-The licenses for most software and other practical works are designed to take 
away your freedom to share and change the works.  By contrast, our General 
Public Licenses are intended to guarantee your freedom to share and change all 
versions of a program--to make sure it remains free software for all its users.
-
-When we speak of free software, we are referring to freedom, not price.  Our 
General Public Licenses are designed to make sure that you have the freedom to 
distribute copies of free software (and charge for them if you wish), that you 
receive source code or can get it if you want it, that you can change the 
software or use pieces of it in new free programs, and that you know you can do 
these things.
-
-Developers that use our General Public Licenses protect your rights with two 
steps: (1) assert copyright on the software, and (2) offer you this License 
which gives you legal permission to copy, distribute and/or modify the software.
-
-A secondary benefit of defending all users' freedom is that improvements made 
in alternate versions of the program, if they receive widespread use, become 
available for other developers to incorporate.  Many developers of free 
software are heartened and encouraged by the resulting cooperation.  However, 
in the case of software used on network servers, this result may fail to come 
about. The GNU General Public License permits making a modified version and 
letting the public access it on a [...]
-
-The GNU Affero General Public License is designed specifically to ensure that, 
in such cases, the modified source code becomes available to the community.  It 
requires the operator of a network server to provide the source code of the 
modified version running there to the users of that server.  Therefore, public 
use of a modified version, on a publicly accessible server, gives the public 
access to the source code of the modified version.
-
-An older license, called the Affero General Public License and published by 
Affero, was designed to accomplish similar goals.  This is a different license, 
not a version of the Affero GPL, but Affero has released a new version of the 
Affero GPL which permits relicensing under this license.
-
-The precise terms and conditions for copying, distribution and modification 
follow.
-
-                       TERMS AND CONDITIONS
-
-0. Definitions.
-
-"This License" refers to version 3 of the GNU Affero General Public License.
-
-"Copyright" also means copyright-like laws that apply to other kinds of works, 
such as semiconductor masks.
-
-"The Program" refers to any copyrightable work licensed under this License.  
Each licensee is addressed as "you".  "Licensees" and "recipients" may be 
individuals or organizations.
-
-To "modify" a work means to copy from or adapt all or part of the work in a 
fashion requiring copyright permission, other than the making of an exact copy. 
 The resulting work is called a "modified version" of the earlier work or a 
work "based on" the earlier work.
-
-A "covered work" means either the unmodified Program or a work based on the 
Program.
-
-To "propagate" a work means to do anything with it that, without permission, 
would make you directly or secondarily liable for infringement under applicable 
copyright law, except executing it on a computer or modifying a private copy.  
Propagation includes copying, distribution (with or without modification), 
making available to the public, and in some countries other activities as well.
-
-To "convey" a work means any kind of propagation that enables other parties to 
make or receive copies.  Mere interaction with a user through a computer 
network, with no transfer of a copy, is not conveying.
-
-An interactive user interface displays "Appropriate Legal Notices" to the 
extent that it includes a convenient and prominently visible feature that (1) 
displays an appropriate copyright notice, and (2) tells the user that there is 
no warranty for the work (except to the extent that warranties are provided), 
that licensees may convey the work under this License, and how to view a copy 
of this License.  If the interface presents a list of user commands or options, 
such as a menu, a promine [...]
-
-1. Source Code.
-The "source code" for a work means the preferred form of the work for making 
modifications to it.  "Object code" means any non-source form of a work.
-
-A "Standard Interface" means an interface that either is an official standard 
defined by a recognized standards body, or, in the case of interfaces specified 
for a particular programming language, one that is widely used among developers 
working in that language.
-
-The "System Libraries" of an executable work include anything, other than the 
work as a whole, that (a) is included in the normal form of packaging a Major 
Component, but which is not part of that Major Component, and (b) serves only 
to enable use of the work with that Major Component, or to implement a Standard 
Interface for which an implementation is available to the public in source code 
form.  A "Major Component", in this context, means a major essential component 
(kernel, window sys [...]
-
-The "Corresponding Source" for a work in object code form means all the source 
code needed to generate, install, and (for an executable work) run the object 
code and to modify the work, including scripts to control those activities.  
However, it does not include the work's System Libraries, or general-purpose 
tools or generally available free programs which are used unmodified in 
performing those activities but which are not part of the work.  For example, 
Corresponding Source includes i [...]
-subprograms and other parts of the work.
-
-The Corresponding Source need not include anything that users can regenerate 
automatically from other parts of the Corresponding Source.
-
-The Corresponding Source for a work in source code form is that same work.
-
-2. Basic Permissions.
-All rights granted under this License are granted for the term of copyright on 
the Program, and are irrevocable provided the stated conditions are met.  This 
License explicitly affirms your unlimited permission to run the unmodified 
Program.  The output from running a covered work is covered by this License 
only if the output, given its content, constitutes a covered work.  This 
License acknowledges your rights of fair use or other equivalent, as provided 
by copyright law.
-
-You may make, run and propagate covered works that you do not convey, without 
conditions so long as your license otherwise remains in force.  You may convey 
covered works to others for the sole purpose of having them make modifications 
exclusively for you, or provide you with facilities for running those works, 
provided that you comply with the terms of this License in conveying all 
material for which you do not control copyright.  Those thus making or running 
the covered works for you m [...]
-
-Conveying under any other circumstances is permitted solely under the 
conditions stated below.  Sublicensing is not allowed; section 10 makes it 
unnecessary.
-
-3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-No covered work shall be deemed part of an effective technological measure 
under any applicable law fulfilling obligations under article 11 of the WIPO 
copyright treaty adopted on 20 December 1996, or similar laws prohibiting or 
restricting circumvention of such measures.
-
-When you convey a covered work, you waive any legal power to forbid 
circumvention of technological measures to the extent such circumvention is 
effected by exercising rights under this License with respect to the covered 
work, and you disclaim any intention to limit operation or modification of the 
work as a means of enforcing, against the work's users, your or third parties' 
legal rights to forbid circumvention of technological measures.
-
-4. Conveying Verbatim Copies.
-You may convey verbatim copies of the Program's source code as you receive it, 
in any medium, provided that you conspicuously and appropriately publish on 
each copy an appropriate copyright notice; keep intact all notices stating that 
this License and any non-permissive terms added in accord with section 7 apply 
to the code; keep intact all notices of the absence of any warranty; and give 
all recipients a copy of this License along with the Program.
-
-You may charge any price or no price for each copy that you convey, and you 
may offer support or warranty protection for a fee.
-
-5. Conveying Modified Source Versions.
-You may convey a work based on the Program, or the modifications to produce it 
from the Program, in the form of source code under the terms of section 4, 
provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified it, and 
giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is released under 
this License and any conditions added under section 7.  This requirement 
modifies the requirement in section 4 to "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this License to 
anyone who comes into possession of a copy.  This License will therefore apply, 
along with any applicable section 7 additional terms, to the whole of the work, 
and all its parts, regardless of how they are packaged.  This License gives no 
permission to license the work in any other way, but it does not invalidate 
such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display 
Appropriate Legal Notices; however, if the Program has interactive interfaces 
that do not display Appropriate Legal Notices, your work need not make them do 
so.
-
-A compilation of a covered work with other separate and independent works, 
which are not by their nature extensions of the covered work, and which are not 
combined with it such as to form a larger program, in or on a volume of a 
storage or distribution medium, is called an "aggregate" if the compilation and 
its resulting copyright are not used to limit the access or legal rights of the 
compilation's users beyond what the individual works permit.  Inclusion of a 
covered work in an aggrega [...]
-
-6. Conveying Non-Source Forms.
-You may convey a covered work in object code form under the terms of sections 
4 and 5, provided that you also convey the machine-readable Corresponding 
Source under the terms of this License, in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product 
(including a physical distribution medium), accompanied by the Corresponding 
Source fixed on a durable physical medium customarily used for software 
interchange.
-
-    b) Convey the object code in, or embodied in, a physical product 
(including a physical distribution medium), accompanied by a written offer, 
valid for at least three years and valid for as long as you offer spare parts 
or customer support for that product model, to give anyone who possesses the 
object code either (1) a copy of the Corresponding Source for all the software 
in the product that is covered by this License, on a durable physical medium 
customarily used for software interc [...]
-
-    c) Convey individual copies of the object code with a copy of the written 
offer to provide the Corresponding Source.  This alternative is allowed only 
occasionally and noncommercially, and only if you received the object code with 
such an offer, in accord with subsection 6b.
-
-    d) Convey the object code by offering access from a designated place 
(gratis or for a charge), and offer equivalent access to the Corresponding 
Source in the same way through the same place at no further charge.  You need 
not require recipients to copy the Corresponding Source along with the object 
code.  If the place to copy the object code is a network server, the 
Corresponding Source may be on a different server (operated by you or a third 
party) that supports equivalent copying f [...]
-
-    e) Convey the object code using peer-to-peer transmission, provided you 
inform other peers where the object code and Corresponding Source of the work 
are being offered to the general public at no charge under subsection 6d.
-
-A separable portion of the object code, whose source code is excluded from the 
Corresponding Source as a System Library, need not be included in conveying the 
object code work.
-
-A "User Product" is either (1) a "consumer product", which means any tangible 
personal property which is normally used for personal, family, or household 
purposes, or (2) anything designed or sold for incorporation into a dwelling.  
In determining whether a product is a consumer product, doubtful cases shall be 
resolved in favor of coverage.  For a particular product received by a 
particular user, "normally used" refers to a typical or common use of that 
class of product, regardless of t [...]
-
-"Installation Information" for a User Product means any methods, procedures, 
authorization keys, or other information required to install and execute 
modified versions of a covered work in that User Product from a modified 
version of its Corresponding Source.  The information must suffice to ensure 
that the continued functioning of the modified object code is in no case 
prevented or interfered with solely because modification has been made.
-
-If you convey an object code work under this section in, or with, or 
specifically for use in, a User Product, and the conveying occurs as part of a 
transaction in which the right of possession and use of the User Product is 
transferred to the recipient in perpetuity or for a fixed term (regardless of 
how the transaction is characterized), the Corresponding Source conveyed under 
this section must be accompanied by the Installation Information.  But this 
requirement does not apply if neith [...]
-
-The requirement to provide Installation Information does not include a 
requirement to continue to provide support service, warranty, or updates for a 
work that has been modified or installed by the recipient, or for the User 
Product in which it has been modified or installed.  Access to a network may be 
denied when the modification itself materially and adversely affects the 
operation of the network or violates the rules and protocols for communication 
across the network.
-
-Corresponding Source conveyed, and Installation Information provided, in 
accord with this section must be in a format that is publicly documented (and 
with an implementation available to the public in source code form), and must 
require no special password or key for unpacking, reading or copying.
-
-7. Additional Terms.
-"Additional permissions" are terms that supplement the terms of this License 
by making exceptions from one or more of its conditions. Additional permissions 
that are applicable to the entire Program shall be treated as though they were 
included in this License, to the extent that they are valid under applicable 
law.  If additional permissions apply only to part of the Program, that part 
may be used separately under those permissions, but the entire Program remains 
governed by this Licens [...]
-
-When you convey a copy of a covered work, you may at your option remove any 
additional permissions from that copy, or from any part of it.  (Additional 
permissions may be written to require their own removal in certain cases when 
you modify the work.)  You may place additional permissions on material, added 
by you to a covered work, for which you have or can give appropriate copyright 
permission.
-
-Notwithstanding any other provision of this License, for material you add to a 
covered work, you may (if authorized by the copyright holders of that material) 
supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the terms 
of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or author 
attributions in that material or in the Appropriate Legal Notices displayed by 
works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or 
requiring that modified versions of such material be marked in reasonable ways 
as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or 
authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some trade 
names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that material by 
anyone who conveys the material (or modified versions of it) with contractual 
assumptions of liability to the recipient, for any liability that these 
contractual assumptions directly impose on those licensors and authors.
-
-All other non-permissive additional terms are considered "further 
restrictions" within the meaning of section 10.  If the Program as you received 
it, or any part of it, contains a notice stating that it is governed by this 
License along with a term that is a further restriction, you may remove that 
term.  If a license document contains a further restriction but permits 
relicensing or conveying under this License, you may add to a covered work 
material governed by the terms of that licens [...]
-
-If you add terms to a covered work in accord with this section, you must 
place, in the relevant source files, a statement of the additional terms that 
apply to those files, or a notice indicating where to find the applicable terms.
-
-Additional terms, permissive or non-permissive, may be stated in the form of a 
separately written license, or stated as exceptions; the above requirements 
apply either way.
-
-8. Termination.
-
-You may not propagate or modify a covered work except as expressly provided 
under this License.  Any attempt otherwise to propagate or modify it is void, 
and will automatically terminate your rights under this License (including any 
patent licenses granted under the third paragraph of section 11).
-
-However, if you cease all violation of this License, then your license from a 
particular copyright holder is reinstated (a) provisionally, unless and until 
the copyright holder explicitly and finally terminates your license, and (b) 
permanently, if the copyright holder fails to notify you of the violation by 
some reasonable means prior to 60 days after the cessation.
-
-Moreover, your license from a particular copyright holder is reinstated 
permanently if the copyright holder notifies you of the violation by some 
reasonable means, this is the first time you have received notice of violation 
of this License (for any work) from that copyright holder, and you cure the 
violation prior to 30 days after your receipt of the notice.
-
-Termination of your rights under this section does not terminate the licenses 
of parties who have received copies or rights from you under this License.  If 
your rights have been terminated and not permanently reinstated, you do not 
qualify to receive new licenses for the same material under section 10.
-
-9. Acceptance Not Required for Having Copies.
-
-You are not required to accept this License in order to receive or run a copy 
of the Program.  Ancillary propagation of a covered work occurring solely as a 
consequence of using peer-to-peer transmission to receive a copy likewise does 
not require acceptance.  However, nothing other than this License grants you 
permission to propagate or modify any covered work.  These actions infringe 
copyright if you do not accept this License.  Therefore, by modifying or 
propagating a covered work, yo [...]
-
-10. Automatic Licensing of Downstream Recipients.
-
-Each time you convey a covered work, the recipient automatically receives a 
license from the original licensors, to run, modify and propagate that work, 
subject to this License.  You are not responsible for enforcing compliance by 
third parties with this License.
-
-An "entity transaction" is a transaction transferring control of an 
organization, or substantially all assets of one, or subdividing an 
organization, or merging organizations.  If propagation of a covered work 
results from an entity transaction, each party to that transaction who receives 
a copy of the work also receives whatever licenses to the work the party's 
predecessor in interest had or could give under the previous paragraph, plus a 
right to possession of the Corresponding Source  [...]
-
-You may not impose any further restrictions on the exercise of the rights 
granted or affirmed under this License.  For example, you may not impose a 
license fee, royalty, or other charge for exercise of rights granted under this 
License, and you may not initiate litigation (including a cross-claim or 
counterclaim in a lawsuit) alleging that any patent claim is infringed by 
making, using, selling, offering for sale, or importing the Program or any 
portion of it.
-
-11. Patents.
-
-A "contributor" is a copyright holder who authorizes use under this License of 
the Program or a work on which the Program is based.  The work thus licensed is 
called the contributor's "contributor version".
-
-A contributor's "essential patent claims" are all patent claims owned or 
controlled by the contributor, whether already acquired or hereafter acquired, 
that would be infringed by some manner, permitted by this License, of making, 
using, or selling its contributor version, but do not include claims that would 
be infringed only as a consequence of further modification of the contributor 
version.  For purposes of this definition, "control" includes the right to 
grant patent sublicenses in a [...]
-
-Each contributor grants you a non-exclusive, worldwide, royalty-free patent 
license under the contributor's essential patent claims, to make, use, sell, 
offer for sale, import and otherwise run, modify and propagate the contents of 
its contributor version.
-
-In the following three paragraphs, a "patent license" is any express agreement 
or commitment, however denominated, not to enforce a patent (such as an express 
permission to practice a patent or covenant not to s ue for patent 
infringement).  To "grant" such a patent license to a party means to make such 
an agreement or commitment not to enforce a patent against the party.
-
-If you convey a covered work, knowingly relying on a patent license, and the 
Corresponding Source of the work is not available for anyone to copy, free of 
charge and under the terms of this License, through a publicly available 
network server or other readily accessible means, then you must either (1) 
cause the Corresponding Source to be so available, or (2) arrange to deprive 
yourself of the benefit of the patent license for this particular work, or (3) 
arrange, in a manner consistent w [...]
-license to downstream recipients.  "Knowingly relying" means you have actual 
knowledge that, but for the patent license, your conveying the covered work in 
a country, or your recipient's use of the covered work in a country, would 
infringe one or more identifiable patents in that country that you have reason 
to believe are valid.
-
-If, pursuant to or in connection with a single transaction or arrangement, you 
convey, or propagate by procuring conveyance of, a covered work, and grant a 
patent license to some of the parties receiving the covered work authorizing 
them to use, propagate, modify or convey a specific copy of the covered work, 
then the patent license you grant is automatically extended to all recipients 
of the covered work and works based on it.
-
-A patent license is "discriminatory" if it does not include within the scope 
of its coverage, prohibits the exercise of, or is conditioned on the 
non-exercise of one or more of the rights that are specifically granted under 
this License.  You may not convey a covered work if you are a party to an 
arrangement with a third party that is in the business of distributing 
software, under which you make payment to the third party based on the extent 
of your activity of conveying the work, and u [...]
-
-Nothing in this License shall be construed as excluding or limiting any 
implied license or other defenses to infringement that may otherwise be 
available to you under applicable patent law.
-
-12. No Surrender of Others' Freedom.
-
-If conditions are imposed on you (whether by court order, agreement or 
otherwise) that contradict the conditions of this License, they do not excuse 
you from the conditions of this License.  If you cannot convey a covered work 
so as to satisfy simultaneously your obligations under this License and any 
other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you to 
collect a royalty for further conveying from those to whom you convey the 
Program, the only way you could satisfy both those terms and this License would 
be to refrain entirely from conveying the Program.
-
-13. Remote Network Interaction; Use with the GNU General Public License.
-
-Notwithstanding any other provision of this License, if you modify the 
Program, your modified version must prominently offer all users interacting 
with it remotely through a computer network (if your version supports such 
interaction) an opportunity to receive the Corresponding Source of your version 
by providing access to the Corresponding Source from a network server at no 
charge, through some standard or customary means of facilitating copying of 
software.  This Corresponding Source s [...]
-
-Notwithstanding any other provision of this License, you have permission to 
link or combine any covered work with a work licensed under version 3 of the 
GNU General Public License into a single combined work, and to convey the 
resulting work.  The terms of this License will continue to apply to the part 
which is the covered work, but the work with which it is combined will remain 
governed by version 3 of the GNU General Public License.
-
-14. Revised Versions of this License.
-
-The Free Software Foundation may publish revised and/or new versions of the 
GNU Affero General Public License from time to time.  Such new versions will be 
similar in spirit to the present version, but may differ in detail to address 
new problems or concerns.
-
-Each version is given a distinguishing version number.  If the Program 
specifies that a certain numbered version of the GNU Affero General Public 
License "or any later version" applies to it, you have the option of following 
the terms and conditions either of that numbered version or of any later 
version published by the Free Software Foundation.  If the Program does not 
specify a version number of the GNU Affero General Public License, you may 
choose any version ever published by the Fr [...]
-
-If the Program specifies that a proxy can decide which future versions of the 
GNU Affero General Public License can be used, that proxy's public statement of 
acceptance of a version permanently authorizes you to choose that version for 
the Program.
-
-Later license versions may give you additional or different permissions.  
However, no additional obligations are imposed on any author or copyright 
holder as a result of your choosing to follow a later version.
-
-15. Disclaimer of Warranty.
-
-THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE 
LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO 
THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM 
PROVE DEFECTIVE, YOU ASSUME TH [...]
-
-16. Limitation of Liability.
-
-IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY 
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS 
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, 
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE 
THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED 
INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE 
PROGRAM TO OPERATE WIT [...]
-
-17. Interpretation of Sections 15 and 16.
-
-If the disclaimer of warranty and limitation of liability provided above 
cannot be given local legal effect according to their terms, reviewing courts 
shall apply local law that most closely approximates an absolute waiver of all 
civil liability in connection with the Program, unless a warranty or assumption 
of liability accompanies a copy of the Program in return for a fee.
-
-END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest possible 
use to the public, the best way to achieve this is to make it free software 
which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program.  It is safest to attach 
them to the start of each source file to most effectively state the exclusion 
of warranty; and each file should have at least the "copyright" line and a 
pointer to where the full notice is found.
-
-     <one line to give the program's name and a brief idea of what it does.>
-     Copyright (C) <year>  <name of author>
-
-     This program is free software: you can redistribute it and/or modify it 
under the terms of the GNU Affero General Public License as published by the 
Free Software Foundation, either version 3 of the License, or (at your option) 
any later version.
-
-     This program is distributed in the hope that it will be useful, but 
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License 
for more details.
-
-     You should have received a copy of the GNU Affero General Public License 
along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If your software can interact with users remotely through a computer network, 
you should also make sure that it provides a way for users to get its source.  
For example, if your program is a web application, its interface could display 
a "Source" link that leads users to an archive of the code.  There are many 
ways you could offer source, and different solutions will be better for 
different programs; see section 13 for the specific requirements.
-
-You should also get your employer (if you work as a programmer) or school, if 
any, to sign a "copyright disclaimer" for the program, if necessary. For more 
information on this, and how to apply and follow the GNU AGPL, see 
<http://www.gnu.org/licenses/>.
\ No newline at end of file
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+Preamble
+
+The GNU Affero General Public License is a free, copyleft license for software
+and other kinds of works, specifically designed to ensure cooperation with the
+community in the case of network server software.
+
+The licenses for most software and other practical works are designed to take
+away your freedom to share and change the works.  By contrast, our General
+Public Licenses are intended to guarantee your freedom to share and change all
+versions of a program--to make sure it remains free software for all its users.
+
+When we speak of free software, we are referring to freedom, not price.  Our
+General Public Licenses are designed to make sure that you have the freedom to
+distribute copies of free software (and charge for them if you wish), that you
+receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs, and that you know you can
+do these things.
+
+Developers that use our General Public Licenses protect your rights with two
+steps: (1) assert copyright on the software, and (2) offer you this License
+which gives you legal permission to copy, distribute and/or modify the 
software.
+
+A secondary benefit of defending all users' freedom is that improvements made
+in alternate versions of the program, if they receive widespread use, become
+available for other developers to incorporate.  Many developers of free
+software are heartened and encouraged by the resulting cooperation.  However,
+in the case of software used on network servers, this result may fail to come
+about. The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its source code
+to the public.
+
+The GNU Affero General Public License is designed specifically to ensure that,
+in such cases, the modified source code becomes available to the community.  It
+requires the operator of a network server to provide the source code of the
+modified version running there to the users of that server.  Therefore, public
+use of a modified version, on a publicly accessible server, gives the public
+access to the source code of the modified version.
+
+An older license, called the Affero General Public License and published by
+Affero, was designed to accomplish similar goals.  This is a different license,
+not a version of the Affero GPL, but Affero has released a new version of the
+Affero GPL which permits relicensing under this license.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+TERMS AND CONDITIONS
+
+0. Definitions.
+
+"This License" refers to version 3 of the GNU Affero General Public License.
+
+"Copyright" also means copyright-like laws that apply to other kinds of works,
+such as semiconductor masks.
+
+"The Program" refers to any copyrightable work licensed under this License.
+Each licensee is addressed as "you".  "Licensees" and "recipients" may be
+individuals or organizations.
+
+To "modify" a work means to copy from or adapt all or part of the work in a
+fashion requiring copyright permission, other than the making of an exact copy.
+ The resulting work is called a "modified version" of the earlier work or a
+work "based on" the earlier work.
+
+A "covered work" means either the unmodified Program or a work based on the
+Program.
+
+To "propagate" a work means to do anything with it that, without permission,
+would make you directly or secondarily liable for infringement under applicable
+copyright law, except executing it on a computer or modifying a private copy.
+Propagation includes copying, distribution (with or without modification),
+making available to the public, and in some countries other activities as well.
+
+To "convey" a work means any kind of propagation that enables other parties to
+make or receive copies.  Mere interaction with a user through a computer
+network, with no transfer of a copy, is not conveying.
+
+An interactive user interface displays "Appropriate Legal Notices" to the
+extent that it includes a convenient and prominently visible feature that
+(1) displays an appropriate copyright notice, and (2) tells the user that
+there is no warranty for the work (except to the extent that warranties are
+provided), that licensees may convey the work under this License, and how to
+view a copy of this License.  If the interface presents a list of user commands
+or options, such as a menu, a prominent item in the list meets this criterion.
+
+1. Source Code.
+
+The "source code" for a work means the preferred form of the work for making
+modifications to it.  "Object code" means any non-source form of a work.
+
+A "Standard Interface" means an interface that either is an official standard
+defined by a recognized standards body, or, in the case of interfaces specified
+for a particular programming language, one that is widely used among developers
+working in that language.
+
+The "System Libraries" of an executable work include anything, other than the
+work as a whole, that (a) is included in the normal form of packaging a Major
+Component, but which is not part of that Major Component, and (b) serves only
+to enable use of the work with that Major Component, or to implement a Standard
+Interface for which an implementation is available to the public in source code
+form.  A "Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system (if any) on
+which the executable work runs, or a compiler used to produce the work, or an
+object code interpreter used to run it.
+
+The "Corresponding Source" for a work in object code form means all the source
+code needed to generate, install, and (for an executable work) run the object
+code and to modify the work, including scripts to control those activities.
+However, it does not include the work's System Libraries, or general-purpose
+tools or generally available free programs which are used unmodified in
+performing those activities but which are not part of the work.  For example,
+Corresponding Source includes interface definition files associated with source
+files for the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require, such as
+by intimate data communication or control flow between those subprograms and
+other parts of the work.
+
+The Corresponding Source need not include anything that users can regenerate
+automatically from other parts of the Corresponding Source.
+
+The Corresponding Source for a work in source code form is that same work.
+
+2. Basic Permissions.
+
+All rights granted under this License are granted for the term of copyright on
+the Program, and are irrevocable provided the stated conditions are met.  This
+License explicitly affirms your unlimited permission to run the unmodified
+Program.  The output from running a covered work is covered by this License
+only if the output, given its content, constitutes a covered work.  This
+License acknowledges your rights of fair use or other equivalent, as provided
+by copyright law.
+
+You may make, run and propagate covered works that you do not convey, without
+conditions so long as your license otherwise remains in force.  You may convey
+covered works to others for the sole purpose of having them make modifications
+exclusively for you, or provide you with facilities for running those works,
+provided that you comply with the terms of this License in conveying all
+material for which you do not control copyright.  Those thus making or running
+the covered works for you must do so exclusively on your behalf, under your
+direction and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+Conveying under any other circumstances is permitted solely under the
+conditions stated below.  Sublicensing is not allowed; section 10 makes it
+unnecessary.
+
+3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+No covered work shall be deemed part of an effective technological measure
+under any applicable law fulfilling obligations under article 11 of the WIPO
+copyright treaty adopted on 20 December 1996, or similar laws prohibiting or
+restricting circumvention of such measures.
+
+When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention is
+effected by exercising rights under this License with respect to the covered
+work, and you disclaim any intention to limit operation or modification of the
+work as a means of enforcing, against the work's users, your or third parties'
+legal rights to forbid circumvention of technological measures.
+
+4. Conveying Verbatim Copies.
+
+You may convey verbatim copies of the Program's source code as you receive it,
+in any medium, provided that you conspicuously and appropriately publish on
+each copy an appropriate copyright notice; keep intact all notices stating
+that this License and any non-permissive terms added in accord with section
+7 apply to the code; keep intact all notices of the absence of any warranty;
+and give all recipients a copy of this License along with the Program.
+
+You may charge any price or no price for each copy that you convey, and you
+may offer support or warranty protection for a fee.
+
+5. Conveying Modified Source Versions.
+
+You may convey a work based on the Program, or the modifications to produce it
+from the Program, in the form of source code under the terms of section 4,
+provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified it, and
+       giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is released under
+       this License and any conditions added under section 7.  This requirement
+       modifies the requirement in section 4 to "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this License to
+       anyone who comes into possession of a copy.  This License will
+       therefore apply, along with any applicable section 7 additional terms,
+       to the whole of the work, and all its parts, regardless of how they are
+       packaged.  This License gives no permission to license the work in any
+       other way, but it does not invalidate such permission if you have
+       separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+       Appropriate Legal Notices; however, if the Program has interactive
+       interfaces that do not display Appropriate Legal Notices, your work
+       need not make them do so.
+
+A compilation of a covered work with other separate and independent works,
+which are not by their nature extensions of the covered work, and which are
+not combined with it such as to form a larger program, in or on a volume of a
+storage or distribution medium, is called an "aggregate" if the compilation
+and its resulting copyright are not used to limit the access or legal rights
+of the compilation's users beyond what the individual works permit.  Inclusion
+of a covered work in an aggregate does not cause this License to apply to the
+other parts of the aggregate.
+
+6. Conveying Non-Source Forms.
+
+You may convey a covered work in object code form under the terms of sections
+4 and 5, provided that you also convey the machine-readable Corresponding
+Source under the terms of this License, in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+       (including a physical distribution medium), accompanied by the
+       Corresponding Source fixed on a durable physical medium customarily
+       used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+       (including a physical distribution medium), accompanied by a written
+       offer, valid for at least three years and valid for as long as you
+       offer spare parts or customer support for that product model, to give
+       anyone who possesses the object code either (1) a copy of the
+       Corresponding Source for all the software in the product that is
+       covered by this License, on a durable physical medium customarily used
+       for software interchange, for a price no more than your reasonable cost
+       of physically performing this conveying of source, or (2) access to
+       copy the Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the written
+       offer to provide the Corresponding Source.  This alternative is allowed
+       only occasionally and noncommercially, and only if you received the
+       object code with such an offer, in accord with subsection 6b.
+
+    d) Convey the object code by offering access from a designated place
+       (gratis or for a charge), and offer equivalent access to the
+       Corresponding Source in the same way through the same place at no
+       further charge.  You need not require recipients to copy the
+       Corresponding Source along with the object code.  If the place to copy
+       the object code is a network server, the Corresponding Source may be on
+       a different server (operated by you or a third party) that supports
+       equivalent copying facilities, provided you maintain clear directions
+       next to the object code saying where to find the Corresponding Source.
+       Regardless of what server hosts the Corresponding Source, you remain
+       obligated to ensure that it is available for as long as needed to
+       satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided you
+       inform other peers where the object code and Corresponding Source of
+       the work are being offered to the general public at no charge under
+       subsection 6d.
+
+A separable portion of the object code, whose source code is excluded from the
+Corresponding Source as a System Library, need not be included in conveying
+the object code work.
+
+A "User Product" is either (1) a "consumer product", which means any tangible
+personal property which is normally used for personal, family, or household
+purposes, or (2) anything designed or sold for incorporation into a dwelling.
+In determining whether a product is a consumer product, doubtful cases shall
+be resolved in favor of coverage.  For a particular product received by a
+particular user, "normally used" refers to a typical or common use of that
+class of product, regardless of the status of the particular user or of the
+way in which the particular user actually uses, or expects or is expected to
+use, the product.  A product is a consumer product regardless of whether the
+product has substantial commercial, industrial or non-consumer uses, unless
+such uses represent the only significant mode of use of the product.
+
+"Installation Information" for a User Product means any methods, procedures,
+authorization keys, or other information required to install and execute
+modified versions of a covered work in that User Product from a modified
+version of its Corresponding Source.  The information must suffice to ensure
+that the continued functioning of the modified object code is in no case
+prevented or interfered with solely because modification has been made.
+
+If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as part of
+a transaction in which the right of possession and use of the User Product is
+transferred to the recipient in perpetuity or for a fixed term (regardless of
+how the transaction is characterized), the Corresponding Source conveyed under
+this section must be accompanied by the Installation Information.  But this
+requirement does not apply if neither you nor any third party retains the
+ability to install modified object code on the User Product (for example, the
+work has been installed in ROM).
+
+The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates for a
+work that has been modified or installed by the recipient, or for the User
+Product in which it has been modified or installed.  Access to a network may
+be denied when the modification itself materially and adversely affects the
+operation of the network or violates the rules and protocols for communication
+across the network.
+
+Corresponding Source conveyed, and Installation Information provided, in
+accord with this section must be in a format that is publicly documented (and
+with an implementation available to the public in source code form), and must
+require no special password or key for unpacking, reading or copying.
+
+7. Additional Terms.
+
+"Additional permissions" are terms that supplement the terms of this License
+by making exceptions from one or more of its conditions. Additional permissions
+that are applicable to the entire Program shall be treated as though they were
+included in this License, to the extent that they are valid under applicable
+law.  If additional permissions apply only to part of the Program, that part
+may be used separately under those permissions, but the entire Program remains
+governed by this License without regard to the additional permissions.
+
+When you convey a copy of a covered work, you may at your option remove any
+additional permissions from that copy, or from any part of it.  (Additional
+permissions may be written to require their own removal in certain cases when
+you modify the work.)  You may place additional permissions on material, added
+by you to a covered work, for which you have or can give appropriate copyright
+permission.
+
+Notwithstanding any other provision of this License, for material you add to a
+covered work, you may (if authorized by the copyright holders of that material)
+supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the terms of
+       sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or author
+       attributions in that material or in the Appropriate Legal Notices
+       displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+       requiring that modified versions of such material be marked in
+       reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or authors
+       of the material; or
+
+    e) Declining to grant rights under trademark law for use of some trade
+       names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that material by
+       anyone who conveys the material (or modified versions of it) with
+       contractual assumptions of liability to the recipient, for any liability
+       that these contractual assumptions directly impose on those licensors
+       and authors.
+
+All other non-permissive additional terms are considered "further restrictions"
+within the meaning of section 10.  If the Program as you received it, or any
+part of it, contains a notice stating that it is governed by this License along
+with a term that is a further restriction, you may remove that term.  If a
+license document contains a further restriction but permits relicensing or
+conveying under this License, you may add to a covered work material governed
+by the terms of that license document, provided that the further restriction
+does not survive such relicensing or conveying.
+
+If you add terms to a covered work in accord with this section, you must place,
+in the relevant source files, a statement of the additional terms that apply
+to those files, or a notice indicating where to find the applicable terms.
+
+Additional terms, permissive or non-permissive, may be stated in the form of a
+separately written license, or stated as exceptions; the above requirements
+apply either way.
+
+8. Termination.
+
+You may not propagate or modify a covered work except as expressly provided
+under this License.  Any attempt otherwise to propagate or modify it is void,
+and will automatically terminate your rights under this License (including any
+patent licenses granted under the third paragraph of section 11).
+
+However, if you cease all violation of this License, then your license from a
+particular copyright holder is reinstated (a) provisionally, unless and until
+the copyright holder explicitly and finally terminates your license, and (b)
+permanently, if the copyright holder fails to notify you of the violation by
+some reasonable means prior to 60 days after the cessation.
+
+Moreover, your license from a particular copyright holder is reinstated
+permanently if the copyright holder notifies you of the violation by some
+reasonable means, this is the first time you have received notice of violation
+of this License (for any work) from that copyright holder, and you cure the
+violation prior to 30 days after your receipt of the notice.
+
+Termination of your rights under this section does not terminate the licenses
+of parties who have received copies or rights from you under this License.
+If your rights have been terminated and not permanently reinstated, you do not
+qualify to receive new licenses for the same material under section 10.
+
+9. Acceptance Not Required for Having Copies.
+
+You are not required to accept this License in order to receive or run a copy
+of the Program.  Ancillary propagation of a covered work occurring solely as a
+consequence of using peer-to-peer transmission to receive a copy likewise does
+not require acceptance.  However, nothing other than this License grants you
+permission to propagate or modify any covered work.  These actions infringe
+copyright if you do not accept this License.  Therefore, by modifying or
+propagating a covered work, you indicate your acceptance of this License to
+do so.
+
+10. Automatic Licensing of Downstream Recipients.
+
+Each time you convey a covered work, the recipient automatically receives a
+license from the original licensors, to run, modify and propagate that work,
+subject to this License.  You are not responsible for enforcing compliance by
+third parties with this License.
+
+An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered work
+results from an entity transaction, each party to that transaction who receives
+a copy of the work also receives whatever licenses to the work the party's
+predecessor in interest had or could give under the previous paragraph, plus a
+right to possession of the Corresponding Source of the work from the
+predecessor in interest, if the predecessor has it or can get it with
+reasonable efforts.
+
+You may not impose any further restrictions on the exercise of the rights
+granted or affirmed under this License.  For example, you may not impose a
+license fee, royalty, or other charge for exercise of rights granted under this
+License, and you may not initiate litigation (including a cross-claim or
+counterclaim in a lawsuit) alleging that any patent claim is infringed by
+making, using, selling, offering for sale, or importing the Program or any
+portion of it.
+
+11. Patents.
+
+A "contributor" is a copyright holder who authorizes use under this License of
+the Program or a work on which the Program is based.  The work thus licensed is
+called the contributor's "contributor version".
+
+A contributor's "essential patent claims" are all patent claims owned or
+controlled by the contributor, whether already acquired or hereafter acquired,
+that would be infringed by some manner, permitted by this License, of making,
+using, or selling its contributor version, but do not include claims that would
+be infringed only as a consequence of further modification of the contributor
+version.  For purposes of this definition, "control" includes the right to
+grant patent sublicenses in a manner consistent with the requirements of this
+License.
+
+Each contributor grants you a non-exclusive, worldwide, royalty-free patent
+license under the contributor's essential patent claims, to make, use, sell,
+offer for sale, import and otherwise run, modify and propagate the contents of
+its contributor version.
+
+In the following three paragraphs, a "patent license" is any express agreement
+or commitment, however denominated, not to enforce a patent (such as an express
+permission to practice a patent or covenant not to s ue for patent
+infringement). To "grant" such a patent license to a party means to make such 
an
+agreement or commitment not to enforce a patent against the party.
+
+If you convey a covered work, knowingly relying on a patent license, and the
+Corresponding Source of the work is not available for anyone to copy, free of
+charge and under the terms of this License, through a publicly available
+network server or other readily accessible means, then you must either
+(1) cause the Corresponding Source to be so available, or (2) arrange to
+deprive yourself of the benefit of the patent license for this particular work,
+or (3) arrange, in a manner consistent with the requirements of this License,
+to extend the patent license to downstream recipients.  "Knowingly relying"
+means you have actual knowledge that, but for the patent license, your
+conveying the covered work in a country, or your recipient's use of the
+covered work in a country, would infringe one or more identifiable patents in
+that country that you have reason to believe are valid.
+
+If, pursuant to or in connection with a single transaction or arrangement, you
+convey, or propagate by procuring conveyance of, a covered work, and grant a
+patent license to some of the parties receiving the covered work authorizing
+them to use, propagate, modify or convey a specific copy of the covered work,
+then the patent license you grant is automatically extended to all recipients
+of the covered work and works based on it.
+
+A patent license is "discriminatory" if it does not include within the scope of
+its coverage, prohibits the exercise of, or is conditioned on the non-exercise
+of one or more of the rights that are specifically granted under this License.
+You may not convey a covered work if you are a party to an arrangement with a
+third party that is in the business of distributing software, under which you
+make payment to the third party based on the extent of your activity of
+conveying the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory patent
+license (a) in connection with copies of the covered work conveyed by you (or
+copies made from those copies), or (b) primarily for and in connection with
+specific products or compilations that contain the covered work, unless you
+entered into that arrangement, or that patent license was granted, prior to
+28 March 2007.
+
+Nothing in this License shall be construed as excluding or limiting any implied
+license or other defenses to infringement that may otherwise be available to
+you under applicable patent law.
+
+12. No Surrender of Others' Freedom.
+
+If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not excuse
+you from the conditions of this License.  If you cannot convey a covered work
+so as to satisfy simultaneously your obligations under this License and any
+other pertinent obligations, then as a consequence you may not convey it at
+all.  For example, if you agree to terms that obligate you to collect a royalty
+for further conveying from those to whom you convey the Program, the only way
+you could satisfy both those terms and this License would be to refrain
+entirely from conveying the Program.
+
+13. Remote Network Interaction; Use with the GNU General Public License.
+
+Notwithstanding any other provision of this License, if you modify the Program,
+your modified version must prominently offer all users interacting with it
+remotely through a computer network (if your version supports such interaction)
+an opportunity to receive the Corresponding Source of your version by providing
+access to the Corresponding Source from a network server at no charge, through
+some standard or customary means of facilitating copying of software.  This
+Corresponding Source shall include the Corresponding Source for any work
+covered by version 3 of the GNU General Public License that is incorporated
+pursuant to the following paragraph.
+
+Notwithstanding any other provision of this License, you have permission to
+link or combine any covered work with a work licensed under version 3 of the
+GNU General Public License into a single combined work, and to convey the
+resulting work.  The terms of this License will continue to apply to the part
+which is the covered work, but the work with which it is combined will remain
+governed by version 3 of the GNU General Public License.
+
+14. Revised Versions of this License.
+
+The Free Software Foundation may publish revised and/or new versions of the GNU
+Affero General Public License from time to time.  Such new versions will be
+similar in spirit to the present version, but may differ in detail to address
+new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies that a certain numbered version of the GNU Affero General Public
+License "or any later version" applies to it, you have the option of
+following the terms and conditions either of that numbered version or of any
+later version published by the Free Software Foundation.  If the Program does
+not specify a version number of the GNU Affero General Public License, you may
+choose any version ever published by the Free Software Foundation.
+
+If the Program specifies that a proxy can decide which future versions of the
+GNU Affero General Public License can be used, that proxy's public statement
+of acceptance of a version permanently authorizes you to choose that version
+for the Program.
+
+Later license versions may give you additional or different permissions.
+However, no additional obligations are imposed on any author or copyright
+holder as a result of your choosing to follow a later version.
+
+15. Disclaimer of Warranty.
+
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE
+LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND,
+EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.
+SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY
+SERVICING, REPAIR OR CORRECTION.
+
+16. Limitation of Liability.
+
+IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
+COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
+INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
+THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
+INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER
+PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+17. Interpretation of Sections 15 and 16.
+
+If the disclaimer of warranty and limitation of liability provided above cannot
+be given local legal effect according to their terms, reviewing courts shall
+apply local law that most closely approximates an absolute waiver of all civil
+liability in connection with the Program, unless a warranty or assumption of
+liability accompanies a copy of the Program in return for a fee.
+
+END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest possible
+use to the public, the best way to achieve this is to make it free software
+which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program.  It is safest to attach
+them to the start of each source file to most effectively state the exclusion
+of warranty; and each file should have at least the "copyright" line and a
+pointer to where the full notice is found.
+
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) <year>  <name of author>
+
+     This program is free software: you can redistribute it and/or modify it
+     under the terms of the GNU Affero General Public License as published by
+     the Free Software Foundation, either version 3 of the License, or (at
+     your option) any later version.
+
+     This program is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+     or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
+     License for more details.
+
+     You should have received a copy of the GNU Affero General Public License
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If your software can interact with users remotely through a computer network,
+you should also make sure that it provides a way for users to get its source.
+For example, if your program is a web application, its interface could display
+a "Source" link that leads users to an archive of the code.  There are many
+ways you could offer source, and different solutions will be better for
+different programs; see section 13 for the specific requirements.
+
+You should also get your employer (if you work as a programmer) or school, if
+any, to sign a "copyright disclaimer" for the program, if necessary. For more
+information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/README.md b/README.md
index 1875501..37b1cb8 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,7 @@ packages are accessible through `GOPATH`. To install the 
dependencies:
 ```bash
 $ go get -u golang.org/x/crypto/...
 $ go get -u golang.org/x/text/...
+$ go get -u github.com/miekg/dns/...
 $ go get -u github.com/bfix/gospel/...
 ```
 
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go
index 1a53941..eaadd98 100644
--- a/src/gnunet/config/config.go
+++ b/src/gnunet/config/config.go
@@ -7,9 +7,10 @@ import (
        "regexp"
        "strings"
 
+       "gnunet/util"
+
        "github.com/bfix/gospel/crypto/ed25519"
        "github.com/bfix/gospel/logger"
-       "gnunet/util"
 )
 
 ///////////////////////////////////////////////////////////////////////
@@ -64,6 +65,7 @@ type Config struct {
 }
 
 var (
+       // Cfg is the global configuration
        Cfg *Config
 )
 
@@ -88,6 +90,8 @@ var (
        rx = regexp.MustCompile("\\$\\{([^\\}]*)\\}")
 )
 
+// substString is a helper function to substitute environment variables
+// with actual values.
 func substString(s string, env map[string]string) string {
        matches := rx.FindAllStringSubmatch(s, -1)
        for _, m := range matches {
@@ -102,7 +106,8 @@ func substString(s string, env map[string]string) string {
        return s
 }
 
-// applySubstitutions
+// applySubstitutions traverses the configuration data structure
+// and applies string substitutions to all string values.
 func applySubstitutions(x interface{}, env map[string]string) {
 
        var process func(v reflect.Value)
@@ -140,10 +145,11 @@ func applySubstitutions(x interface{}, env 
map[string]string) {
                        }
                }
        }
-
+       // start processing at the top-level structure
        v := reflect.ValueOf(x)
        switch v.Kind() {
        case reflect.Ptr:
+               // indirect top-level
                e := v.Elem()
                if e.IsValid() {
                        process(e)
@@ -151,6 +157,7 @@ func applySubstitutions(x interface{}, env 
map[string]string) {
                        logger.Printf(logger.ERROR, "[config] 'nil' pointer 
encountered")
                }
        case reflect.Struct:
+               // direct top-level
                process(v)
        }
 }
diff --git a/src/gnunet/core/peer.go b/src/gnunet/core/peer.go
index ac2d980..d0b3a4c 100644
--- a/src/gnunet/core/peer.go
+++ b/src/gnunet/core/peer.go
@@ -3,30 +3,25 @@ package core
 import (
        "fmt"
 
-       "github.com/bfix/gospel/crypto/ed25519"
        "gnunet/message"
        "gnunet/util"
-)
 
-/*
-type Peer interface {
-       GetID() []byte
-       GetIDString() string
-       GetAddressList() []*util.Address
-       Sign(msg []byte) ([]byte, error)
-       Verify(msg, sig []byte) bool
-}
-*/
+       "github.com/bfix/gospel/crypto/ed25519"
+)
 
+// Peer represents a node in the GNUnet P2P network.
 type Peer struct {
-       pub      *ed25519.PublicKey
-       idString string
-       addrList []*util.Address
-       prv      *ed25519.PrivateKey      // long-term signing key
+       prv      *ed25519.PrivateKey      // node private key (long-term 
signing key)
+       pub      *ed25519.PublicKey       // node public key (=identifier)
+       idString string                   // node identifier as string
+       addrList []*util.Address          // list of addresses associated with 
node
        ephPrv   *ed25519.PrivateKey      // ephemeral signing key
        ephMsg   *message.EphemeralKeyMsg // ephemeral signing key message
 }
 
+// NewPeer instantiates a new peer object from given data. If a local peer
+// is created, the data is the seed for generating the private key of the node;
+// for a remote peer the data is the binary representation of its public key.
 func NewPeer(data []byte, local bool) (p *Peer, err error) {
        p = new(Peer)
        if local {
@@ -45,42 +40,52 @@ func NewPeer(data []byte, local bool) (p *Peer, err error) {
        return
 }
 
+// EphKeyMsg returns a new initialized message to negotiate session keys.
 func (p *Peer) EphKeyMsg() *message.EphemeralKeyMsg {
        return p.ephMsg
 }
 
+// SetEphKeyMsg saves a template for new key negotiation messages.
 func (p *Peer) SetEphKeyMsg(msg *message.EphemeralKeyMsg) {
        p.ephMsg = msg
 }
 
+// EphPrvKey returns the current ephemeral private key.
 func (p *Peer) EphPrvKey() *ed25519.PrivateKey {
        return p.ephPrv
 }
 
+// PrvKey return the private key of the node.
 func (p *Peer) PrvKey() *ed25519.PrivateKey {
        return p.prv
 }
 
+// PubKey return the public key of the node.
 func (p *Peer) PubKey() *ed25519.PublicKey {
        return p.pub
 }
 
+// GetID returns the node ID (public key) in binary format
 func (p *Peer) GetID() []byte {
        return p.pub.Bytes()
 }
 
+// GetIDString returns the string representation of the public key of the node.
 func (p *Peer) GetIDString() string {
        return p.idString
 }
 
+// GetAddressList returns a list of addresses associated with this peer.
 func (p *Peer) GetAddressList() []*util.Address {
        return p.addrList
 }
 
+// AddAddress adds a new address for a node.
 func (p *Peer) AddAddress(a *util.Address) {
        p.addrList = append(p.addrList, a)
 }
 
+// Sign a message with the (long-term) private key.
 func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) {
        if p.prv == nil {
                return nil, fmt.Errorf("No private key")
@@ -88,6 +93,7 @@ func (p *Peer) Sign(msg []byte) (*ed25519.EdSignature, error) 
{
        return p.prv.EdSign(msg)
 }
 
+// Verify a message signature with the public key of a peer.
 func (p *Peer) Verify(msg []byte, sig *ed25519.EdSignature) (bool, error) {
        return p.pub.EdVerify(msg, sig)
 }
diff --git a/src/gnunet/crypto/gns.go b/src/gnunet/crypto/gns.go
new file mode 100644
index 0000000..c6a8cd6
--- /dev/null
+++ b/src/gnunet/crypto/gns.go
@@ -0,0 +1,35 @@
+package crypto
+
+import (
+       "crypto/sha256"
+       "crypto/sha512"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "golang.org/x/crypto/hkdf"
+)
+
+// DeriveBlockKey returns a symmetric key and initialization vector to 
decipher a GNS block.
+func DeriveBlockKey(label string, pub *ed25519.PublicKey) (iv *SymmetricIV, 
skey *SymmetricKey) {
+       // generate symmetric key
+       prk := hkdf.Extract(sha512.New, pub.Bytes(), []byte("gns-aes-ctx-key"))
+       rdr := hkdf.Expand(sha256.New, prk, []byte(label))
+       skey = NewSymmetricKey()
+       rdr.Read(skey.AESKey)
+       rdr.Read(skey.TwofishKey)
+
+       // generate initialization vector
+       prk = hkdf.Extract(sha512.New, pub.Bytes(), []byte("gns-aes-ctx-iv"))
+       rdr = hkdf.Expand(sha256.New, prk, []byte(label))
+       iv = NewSymmetricIV()
+       rdr.Read(iv.AESIv)
+       rdr.Read(iv.TwofishIv)
+       return
+}
+
+// DecryptBlock for a given zone and label.
+func DecryptBlock(data []byte, zoneKey *ed25519.PublicKey, label string) (out 
[]byte, err error) {
+       // derive key material for decryption
+       iv, skey := DeriveBlockKey(label, zoneKey)
+       // perform decryption
+       return SymmetricDecrypt(data, skey, iv)
+}
diff --git a/src/gnunet/crypto/gns_test.go b/src/gnunet/crypto/gns_test.go
new file mode 100644
index 0000000..7f3cdb8
--- /dev/null
+++ b/src/gnunet/crypto/gns_test.go
@@ -0,0 +1,150 @@
+package crypto
+
+import (
+       "bytes"
+       "encoding/hex"
+       "testing"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+)
+
+var (
+       PUB = []byte{
+               0x93, 0x34, 0x71, 0xF6, 0x99, 0x19, 0x0C, 0x62,
+               0x85, 0xC7, 0x9B, 0x83, 0x9D, 0xCA, 0x83, 0x91,
+               0x38, 0xFA, 0x87, 0xFB, 0xB8, 0xD4, 0xF6, 0xF0,
+               0xF0, 0x4B, 0x7F, 0x0A, 0x48, 0xBF, 0x95, 0xF7,
+       }
+       LABEL = "home"
+)
+
+func TestDeriveBlockKey(t *testing.T) {
+       var (
+               SKEY = []byte{
+                       0x1D, 0x86, 0x8E, 0xF7, 0x30, 0x96, 0x3B, 0x39,
+                       0x66, 0xE6, 0x49, 0xD8, 0xF1, 0x13, 0x18, 0x39,
+                       0x8A, 0x7A, 0xB0, 0xF3, 0xDC, 0xF6, 0xE7, 0x2A,
+                       0xF6, 0x65, 0xDE, 0x86, 0x47, 0x7B, 0x20, 0x1B,
+
+                       0x21, 0xA6, 0xFA, 0x55, 0x7C, 0x29, 0xF5, 0x94,
+                       0x8E, 0x9A, 0x80, 0xB0, 0xB6, 0xD5, 0x4D, 0x38,
+                       0x0E, 0x6A, 0x0F, 0x42, 0x4B, 0x27, 0xBB, 0x6A,
+                       0x1E, 0xD1, 0x33, 0x08, 0xD6, 0x2E, 0x21, 0x8C,
+               }
+               IV = []byte{
+                       0xAC, 0x18, 0x03, 0xB7, 0x8B, 0x1E, 0x09, 0xA9,
+                       0xD0, 0x20, 0x47, 0x2B, 0x1B, 0x23, 0xE8, 0x24,
+
+                       0xC9, 0x23, 0x9E, 0x61, 0x3A, 0x8D, 0x95, 0xA9,
+                       0x3F, 0x6C, 0x1C, 0xC8, 0xCB, 0xD1, 0xBD, 0x6B,
+               }
+       )
+
+       iv, skey := DeriveBlockKey(LABEL, ed25519.NewPublicKeyFromBytes(PUB))
+
+       if bytes.Compare(IV[:16], iv.AESIv) != 0 {
+               t.Logf("AES_IV(computed) = %s\n", hex.EncodeToString(iv.AESIv))
+               t.Logf("AES_IV(expected) = %s\n", hex.EncodeToString(IV[:16]))
+               t.Fatal("AES IV mismatch")
+       }
+       if bytes.Compare(IV[16:], iv.TwofishIv) != 0 {
+               t.Logf("Twofish_IV(computed) = %s\n", 
hex.EncodeToString(iv.TwofishIv))
+               t.Logf("Twofish_IV(expected) = %s\n", 
hex.EncodeToString(IV[16:]))
+               t.Fatal("Twofish IV mismatch")
+       }
+
+       if bytes.Compare(SKEY[:32], skey.AESKey) != 0 {
+               t.Logf("AES_KEY(computed) = %s\n", 
hex.EncodeToString(skey.AESKey))
+               t.Logf("AES_KEY(expected) = %s\n", 
hex.EncodeToString(SKEY[:32]))
+               t.Fatal("AES KEY mismatch")
+       }
+       if bytes.Compare(SKEY[32:], skey.TwofishKey) != 0 {
+               t.Logf("Twofish_KEY(computed) = %s\n", 
hex.EncodeToString(skey.TwofishKey))
+               t.Logf("Twofish_KEY(expected) = %s\n", 
hex.EncodeToString(SKEY[32:]))
+               t.Fatal("Twofish KEY mismatch")
+       }
+}
+
+func TestDecryptBlock(t *testing.T) {
+       var (
+               DATA = []byte{
+                       0xAC, 0xA5, 0x3C, 0x55, 0x63, 0x21, 0x31, 0x1F,
+                       0x11, 0x6E, 0xEF, 0x48, 0xED, 0x53, 0x46, 0x31,
+                       0x7C, 0x50, 0xFB, 0x6B, 0xA6, 0xC8, 0x6C, 0x46,
+                       0x1E, 0xE3, 0xCA, 0x45, 0xCD, 0x5B, 0xD6, 0x86,
+                       0x42, 0x87, 0xEF, 0x18, 0xCE, 0x8E, 0x83, 0x21,
+                       0x04, 0xCB, 0xCF, 0x40, 0x7E, 0x0F, 0x51, 0x54,
+                       0xE2, 0x3C, 0xDE, 0xE9, 0x22, 0x00, 0xFF, 0x40,
+                       0xBB, 0x53, 0xE3, 0x69, 0x99, 0x92, 0x47, 0x97,
+                       0xF0, 0x4E, 0x3B, 0x70,
+               }
+               OUT = []byte{
+                       0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0xAD, 0x0E,
+                       0x60, 0x28, 0xFE, 0x80, 0x00, 0x00, 0x00, 0x10,
+                       0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,
+                       0x0A, 0x68, 0x6F, 0x69, 0x2D, 0x70, 0x6F, 0x6C,
+                       0x6C, 0x6F, 0x69, 0x03, 0x6F, 0x72, 0x67, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00,
+               }
+       )
+
+       out, err := DecryptBlock(DATA, ed25519.NewPublicKeyFromBytes(PUB), 
LABEL)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if bytes.Compare(out, OUT) != 0 {
+               t.Logf("Decrypt(computed) = %s\n", hex.EncodeToString(out))
+               t.Logf("Decrypt(expected) = %s\n", hex.EncodeToString(OUT))
+               t.Fatal("Decryptions failed")
+       }
+}
+
+func TestVerifyBlock(t *testing.T) {
+       var (
+               SIGNED = []byte{
+                       0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x0f,
+                       0x00, 0x05, 0xad, 0x0e, 0x60, 0x28, 0xfe, 0x80,
+                       0xac, 0xa5, 0x3c, 0x55, 0x63, 0x21, 0x31, 0x1f,
+                       0x11, 0x6e, 0xef, 0x48, 0xed, 0x53, 0x46, 0x31,
+                       0x7c, 0x50, 0xfb, 0x6b, 0xa6, 0xc8, 0x6c, 0x46,
+                       0x1e, 0xe3, 0xca, 0x45, 0xcd, 0x5b, 0xd6, 0x86,
+                       0x42, 0x87, 0xef, 0x18, 0xce, 0x8e, 0x83, 0x21,
+                       0x04, 0xcb, 0xcf, 0x40, 0x7e, 0x0f, 0x51, 0x54,
+                       0xe2, 0x3c, 0xde, 0xe9, 0x22, 0x00, 0xff, 0x40,
+                       0xbb, 0x53, 0xe3, 0x69, 0x99, 0x92, 0x47, 0x97,
+                       0xf0, 0x4e, 0x3b, 0x70,
+               }
+               SIG = []byte{
+                       0x09, 0xc9, 0x6a, 0xda, 0x69, 0xce, 0x7c, 0x91,
+                       0xbd, 0xa4, 0x59, 0xdc, 0xc9, 0x76, 0xf4, 0x6c,
+                       0x62, 0xb7, 0x79, 0x3f, 0x94, 0xb2, 0xf6, 0xf0,
+                       0x90, 0x17, 0x4e, 0x2f, 0x68, 0x49, 0xf8, 0xcc,
+
+                       0x0b, 0x77, 0x32, 0x32, 0x28, 0x77, 0x2d, 0x2a,
+                       0x31, 0x31, 0xc1, 0x2c, 0x44, 0x18, 0xf2, 0x5f,
+                       0x1a, 0xe9, 0x8b, 0x2e, 0x65, 0xca, 0x1d, 0xe8,
+                       0x22, 0x82, 0x6a, 0x06, 0xe0, 0x6a, 0x5a, 0xe5,
+               }
+               PUB = []byte{
+                       0x26, 0x84, 0x1b, 0x24, 0x35, 0xa4, 0x63, 0xe9,
+                       0xf0, 0x48, 0xae, 0x3e, 0xf7, 0xe8, 0x1b, 0xca,
+                       0x55, 0x9f, 0x4c, 0x1e, 0x16, 0x18, 0xa6, 0xd3,
+                       0x5b, 0x91, 0x0d, 0x54, 0x31, 0x6e, 0xbf, 0x97,
+               }
+       )
+       sig, err := ed25519.NewEcSignatureFromBytes(SIG)
+       if err != nil {
+               t.Fatal(err)
+       }
+       dkey := ed25519.NewPublicKeyFromBytes(PUB)
+       ok, err := dkey.EcVerify(SIGNED, sig)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if !ok {
+               t.Fatal("EcDSA verify failed")
+       }
+}
diff --git a/src/gnunet/crypto/hash.go b/src/gnunet/crypto/hash.go
index 87e3941..0fe7c7e 100644
--- a/src/gnunet/crypto/hash.go
+++ b/src/gnunet/crypto/hash.go
@@ -6,16 +6,19 @@ import (
        "gnunet/util"
 )
 
+// HashCode is the result of a 512-bit hash function (SHA-512)
 type HashCode struct {
        Bits []byte `size:"64"`
 }
 
+// NewHashCode creates a new, uninitalized hash value
 func NewHashCode() *HashCode {
        return &HashCode{
                Bits: make([]byte, 64),
        }
 }
 
+// Hash returns the SHA-512 hash value of a given blob
 func Hash(data []byte) *HashCode {
        val := sha512.Sum512(data)
        return &HashCode{
diff --git a/src/gnunet/crypto/key_derivation.go 
b/src/gnunet/crypto/key_derivation.go
index 575f511..976db2a 100644
--- a/src/gnunet/crypto/key_derivation.go
+++ b/src/gnunet/crypto/key_derivation.go
@@ -9,6 +9,7 @@ import (
        "golang.org/x/crypto/hkdf"
 )
 
+// Curve parameters
 var (
        ED25519_N = ed25519.GetCurve().N
 )
diff --git a/src/gnunet/crypto/key_exchange.go 
b/src/gnunet/crypto/key_exchange.go
index 0efa441..1403249 100644
--- a/src/gnunet/crypto/key_exchange.go
+++ b/src/gnunet/crypto/key_exchange.go
@@ -4,8 +4,8 @@ import (
        "github.com/bfix/gospel/crypto/ed25519"
 )
 
-// SharedSecret computes a 64 byte shared secret
-// between (prvA,pubB) and (prvB,pubA).
+// SharedSecret computes a 64 byte shared secret between (prvA,pubB)
+// and (prvB,pubA) by a Diffie-Hellman-like scheme.
 func SharedSecret(prv *ed25519.PrivateKey, pub *ed25519.PublicKey) *HashCode {
        ss := pub.Mult(prv.D).Q.X().Bytes()
        return Hash(ss)
diff --git a/src/gnunet/crypto/signature.go b/src/gnunet/crypto/signature.go
index 9a69b56..ba8e535 100644
--- a/src/gnunet/crypto/signature.go
+++ b/src/gnunet/crypto/signature.go
@@ -1,5 +1,6 @@
 package crypto
 
+// SignaturePurpose is the GNUnet data structure used as header for signed 
data.
 type SignaturePurpose struct {
        Size    uint32 `order:"big"` // How many bytes are signed?
        Purpose uint32 `order:"big"` // Signature purpose
diff --git a/src/gnunet/crypto/symmetric.go b/src/gnunet/crypto/symmetric.go
index 82116a4..5607d3a 100644
--- a/src/gnunet/crypto/symmetric.go
+++ b/src/gnunet/crypto/symmetric.go
@@ -8,11 +8,17 @@ import (
        "golang.org/x/crypto/twofish"
 )
 
+// Symmetric encryption in GNUnet uses a two-layer scheme:
+// * Encryption: OUT = twofish_cfb(aes_cfb(IN))
+// * Decryption: OUT = aes_cfb(twofish_cfb(IN))
+
+// SymmetricKey is a key for the GNUnet-specific two-layer encryption scheme.
 type SymmetricKey struct {
-       AESKey     []byte `size:"32"`
-       TwofishKey []byte `size:"32"`
+       AESKey     []byte `size:"32"` // key for AES-CFB
+       TwofishKey []byte `size:"32"` // key for Twofish-CFB
 }
 
+// NewSymmetricKey generates a new (random) symmetric key.
 func NewSymmetricKey() *SymmetricKey {
        skey := &SymmetricKey{
                AESKey:     make([]byte, 32),
@@ -23,11 +29,14 @@ func NewSymmetricKey() *SymmetricKey {
        return skey
 }
 
+// SymmetricIV is an initialization vector for the GNUnet-specific two-layer
+// encryption scheme.
 type SymmetricIV struct {
-       AESIv     []byte `size:"16"`
-       TwofishIv []byte `size:"16"`
+       AESIv     []byte `size:"16"` // IV for AES-CFB
+       TwofishIv []byte `size:"16"` // IV for Twofish-CFB
 }
 
+// NewSymmetricIV generates a new (random) initialization vector.
 func NewSymmetricIV() *SymmetricIV {
        iv := &SymmetricIV{
                AESIv:     make([]byte, 16),
@@ -38,6 +47,7 @@ func NewSymmetricIV() *SymmetricIV {
        return iv
 }
 
+// SymmetricDecrypt decrypts the data with given key and initialization vector.
 func SymmetricDecrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) 
([]byte, error) {
        // Decrypt with Twofish CFB stream cipher
        tf, err := twofish.NewCipher(skey.TwofishKey)
@@ -58,6 +68,7 @@ func SymmetricDecrypt(data []byte, skey *SymmetricKey, iv 
*SymmetricIV) ([]byte,
        return out, nil
 }
 
+// SymmetricEncrypt encrypts the data with given key and initialization vector.
 func SymmetricEncrypt(data []byte, skey *SymmetricKey, iv *SymmetricIV) 
([]byte, error) {
        // Encrypt with AES CFB stream cipher
        aes, err := aes.NewCipher(skey.AESKey)
diff --git a/src/gnunet/enums/dht.go b/src/gnunet/enums/dht.go
index 87c36a3..8f004d7 100644
--- a/src/gnunet/enums/dht.go
+++ b/src/gnunet/enums/dht.go
@@ -1,5 +1,6 @@
 package enums
 
+// DHT flags and settings
 var (
        DHT_RO_NONE                   = 0  // Default.  Do nothing special.
        DHT_RO_DEMULTIPLEX_EVERYWHERE = 1  // Each peer along the way should 
look at 'enc'
@@ -10,3 +11,25 @@ var (
 
        DHT_GNS_REPLICATION_LEVEL = 10
 )
+
+// DHT block types
+var (
+       BLOCK_TYPE_ANY            = 0  // Any type of block, used as a wildcard 
when searching.
+       BLOCK_TYPE_FS_DBLOCK      = 1  // Data block (leaf) in the CHK tree.
+       BLOCK_TYPE_FS_IBLOCK      = 2  // Inner block in the CHK tree.
+       BLOCK_TYPE_FS_KBLOCK      = 3  // Legacy type, no longer in use.
+       BLOCK_TYPE_FS_SBLOCK      = 4  // Legacy type, no longer in use.
+       BLOCK_TYPE_FS_NBLOCK      = 5  // Legacy type, no longer in use.
+       BLOCK_TYPE_FS_ONDEMAND    = 6  // Type of a block representing a block 
to be encoded on demand from disk.
+       BLOCK_TYPE_DHT_HELLO      = 7  // Type of a block that contains a HELLO 
for a peer
+       BLOCK_TYPE_TEST           = 8  // Block for testing.
+       BLOCK_TYPE_FS_UBLOCK      = 9  // Type of a block representing any type 
of search result (universal).
+       BLOCK_TYPE_DNS            = 10 // Block for storing DNS exit service 
advertisements.
+       BLOCK_TYPE_GNS_NAMERECORD = 11 // Block for storing record data
+       BLOCK_TYPE_REVOCATION     = 12 // Block type for a revocation message 
by which a key is revoked.
+
+       BLOCK_TYPE_REGEX             = 22 // Block to store a cadet regex state
+       BLOCK_TYPE_REGEX_ACCEPT      = 23 // Block to store a cadet regex 
accepting state
+       BLOCK_TYPE_SET_TEST          = 24 // Block for testing set/consensus.
+       BLOCK_TYPE_CONSENSUS_ELEMENT = 25 // Block type for consensus elements.
+)
diff --git a/src/gnunet/enums/gns.go b/src/gnunet/enums/gns.go
index 2de5048..d5b1f7a 100644
--- a/src/gnunet/enums/gns.go
+++ b/src/gnunet/enums/gns.go
@@ -1,5 +1,6 @@
 package enums
 
+// GNS constants
 var (
        GNS_MAX_BLOCK_SIZE = (63 * 1024) // Maximum size of a value that can be 
stored in a GNS block.
 
diff --git a/src/gnunet/message/message.go b/src/gnunet/message/message.go
index 6b49a55..e792ae8 100644
--- a/src/gnunet/message/message.go
+++ b/src/gnunet/message/message.go
@@ -6,27 +6,35 @@ import (
        "github.com/bfix/gospel/data"
 )
 
+// Error codes
 var (
        ErrMsgHeaderTooSmall = errors.New("Message header too small")
 )
 
+// Message is an interface for all GNUnet-specific messages.
 type Message interface {
        Header() *MessageHeader
 }
 
+// MessageHeader encapsulates the common part of all GNUnet messages (at the
+// beginning of the data).
 type MessageHeader struct {
        MsgSize uint16 `order:"big"`
        MsgType uint16 `order:"big"`
 }
 
+// Size returns the total size of the message (header + body)
 func (mh *MessageHeader) Size() uint16 {
        return mh.MsgSize
 }
 
+// Type returns the message type (defines the layout of the body data)
 func (mh *MessageHeader) Type() uint16 {
        return mh.MsgType
 }
 
+// GetMsgHeader returns the header of a message from a byte array (as the
+// serialized form).
 func GetMsgHeader(b []byte) (mh *MessageHeader, err error) {
        if b == nil || len(b) < 4 {
                return nil, ErrMsgHeaderTooSmall
diff --git a/src/gnunet/message/msg_core.go b/src/gnunet/message/msg_core.go
index 0ddbd8b..538fcea 100644
--- a/src/gnunet/message/msg_core.go
+++ b/src/gnunet/message/msg_core.go
@@ -5,21 +5,25 @@ import (
        "fmt"
        "time"
 
-       "github.com/bfix/gospel/crypto/ed25519"
-       "github.com/bfix/gospel/data"
+       "gnunet/crypto"
        "gnunet/enums"
        "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/data"
 )
 
+// EphKeyBlock defines the layout of signed ephemeral key with attributes.
 type EphKeyBlock struct {
-       SignSize     uint32            `order:"big"` // length of signed block
-       SigPurpose   uint32            `order:"big"` // signature purpose: 
SIG_ECC_KEY
-       CreateTime   util.AbsoluteTime // Time of key creation
-       ExpireTime   util.RelativeTime // Time to live for key
-       EphemeralKey []byte            `size:"32"` // Ephemeral EdDSA public key
-       PeerID       *util.PeerID      // Peer identity (EdDSA public key)
+       Purpose      *crypto.SignaturePurpose // signature purpose: SIG_ECC_KEY
+       CreateTime   util.AbsoluteTime        // Time of key creation
+       ExpireTime   util.RelativeTime        // Time to live for key
+       EphemeralKey []byte                   `size:"32"` // Ephemeral EdDSA 
public key
+       PeerID       *util.PeerID             // Peer identity (EdDSA public 
key)
 }
 
+// EphemeralKeyMsg announces a new transient key for a peer. The key is signed
+// by the issuing peer.
 type EphemeralKeyMsg struct {
        MsgSize      uint16 `order:"big"` // total size of message
        MsgType      uint16 `order:"big"` // CORE_EPHEMERAL_KEY (88)
@@ -28,6 +32,7 @@ type EphemeralKeyMsg struct {
        SignedBlock  *EphKeyBlock
 }
 
+// NewEphemeralKeyMsg creates an empty message for key announcement.
 func NewEphemeralKeyMsg() *EphemeralKeyMsg {
        return &EphemeralKeyMsg{
                MsgSize:      160,
@@ -35,8 +40,10 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg {
                SenderStatus: 1,
                Signature:    make([]byte, 64),
                SignedBlock: &EphKeyBlock{
-                       SignSize:     88,
-                       SigPurpose:   enums.SIG_ECC_KEY,
+                       Purpose: &crypto.SignaturePurpose{
+                               Size:    88,
+                               Purpose: enums.SIG_ECC_KEY,
+                       },
                        CreateTime:   util.AbsoluteTimeNow(),
                        ExpireTime:   util.NewRelativeTime(12 * time.Hour),
                        EphemeralKey: make([]byte, 32),
@@ -45,6 +52,7 @@ func NewEphemeralKeyMsg() *EphemeralKeyMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *EphemeralKeyMsg) String() string {
        return 
fmt.Sprintf("EphKeyMsg{peer=%s,ephkey=%s,create=%s,expire=%s,status=%d}",
                util.EncodeBinaryToString(m.SignedBlock.PeerID.Key),
@@ -58,10 +66,13 @@ func (msg *EphemeralKeyMsg) Header() *MessageHeader {
        return &MessageHeader{msg.MsgSize, msg.MsgType}
 }
 
+// Public extracts the public key of an announcing peer.
 func (m *EphemeralKeyMsg) Public() *ed25519.PublicKey {
        return ed25519.NewPublicKeyFromBytes(m.SignedBlock.PeerID.Key)
 }
 
+// Verify the integrity of the message data using the public key of the
+// announcing peer.
 func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) (bool, error) {
        data, err := data.Marshal(m.SignedBlock)
        if err != nil {
@@ -74,6 +85,8 @@ func (m *EphemeralKeyMsg) Verify(pub *ed25519.PublicKey) 
(bool, error) {
        return pub.EdVerify(data, sig)
 }
 
+// NewEphemeralKey creates a new ephemeral key signed by a long-term private
+// key and the corresponding GNUnet message to announce the new key.
 func NewEphemeralKey(peerId []byte, ltPrv *ed25519.PrivateKey) 
(*ed25519.PrivateKey, *EphemeralKeyMsg, error) {
        msg := NewEphemeralKeyMsg()
        copy(msg.SignedBlock.PeerID.Key, peerId)
diff --git a/src/gnunet/message/msg_dht.go b/src/gnunet/message/msg_dht.go
index bd3475a..b1d28ba 100644
--- a/src/gnunet/message/msg_dht.go
+++ b/src/gnunet/message/msg_dht.go
@@ -51,6 +51,7 @@ func (m *DHTClientGetMsg) SetXQuery(xq []byte) []byte {
        return prev
 }
 
+// String returns a human-readable representation of the message.
 func (m *DHTClientGetMsg) String() string {
        return 
fmt.Sprintf("DHTClientGetMsg{Id:%d,Type=%d,Options=%d,Repl=%d,Key=%s}",
                m.Id, m.Type, m.Options, m.ReplLevel, 
hex.EncodeToString(m.Key.Bits))
@@ -98,6 +99,7 @@ func NewDHTClientResultMsg(key *crypto.HashCode) 
*DHTClientResultMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *DHTClientResultMsg) String() string {
        return fmt.Sprintf("DHTClientResultMsg{id:%d,expire=%s}", m.Id, 
m.Expire)
 }
diff --git a/src/gnunet/message/msg_gns.go b/src/gnunet/message/msg_gns.go
index 0ff34be..b01c410 100644
--- a/src/gnunet/message/msg_gns.go
+++ b/src/gnunet/message/msg_gns.go
@@ -3,9 +3,10 @@ package message
 import (
        "fmt"
 
-       "github.com/bfix/gospel/logger"
        "gnunet/enums"
        "gnunet/util"
+
+       "github.com/bfix/gospel/logger"
 )
 
 //----------------------------------------------------------------------
@@ -38,13 +39,13 @@ func NewGNSLookupMsg() *GNSLookupMsg {
        }
 }
 
-// SetName
+// SetName appends the name to lookup to the message
 func (m *GNSLookupMsg) SetName(name string) {
        m.Name = util.Clone(append([]byte(name), 0))
        m.MsgSize = uint16(48 + len(m.Name))
 }
 
-// GetName
+// GetName returns the name to lookup from the message
 func (m *GNSLookupMsg) GetName() string {
        size := len(m.Name)
        if m.Name[size-1] != 0 {
@@ -55,7 +56,7 @@ func (m *GNSLookupMsg) GetName() string {
        return string(m.Name[:size])
 }
 
-// String
+// String returns a human-readable representation of the message.
 func (m *GNSLookupMsg) String() string {
        return fmt.Sprintf(
                "GNSLookupMsg{Id=%d,Zone=%s,Options=%d,Type=%d,Name=%s}",
@@ -72,6 +73,8 @@ func (msg *GNSLookupMsg) Header() *MessageHeader {
 // GNS_LOOKUP_RESULT
 //----------------------------------------------------------------------
 
+// GNSResourceRecord is the GNUnet-specific representation of resource
+// records (not to be confused with DNS resource records).
 type GNSResourceRecord struct {
        Expires util.AbsoluteTime // Expiration time for the record
        Size    uint32            `order:"big"` // Number of bytes in 'Data'
@@ -80,6 +83,7 @@ type GNSResourceRecord struct {
        Data    []byte            `size:"Size"` // Record data
 }
 
+// String returns a human-readable representation of the message.
 func (r *GNSResourceRecord) String() string {
        return 
fmt.Sprintf("GNSResourceRecord{type=%s,expire=%s,flags=%d,size=%d}",
                enums.GNS_TYPE[int(r.Type)], r.Expires, r.Flags, r.Size)
@@ -105,7 +109,7 @@ func NewGNSLookupResultMsg(id uint32) *GNSLookupResultMsg {
        }
 }
 
-// AddRecord
+// AddRecord adds a GNS resource recordto the response message.
 func (m *GNSLookupResultMsg) AddRecord(rec *GNSResourceRecord) error {
        recSize := 20 + int(rec.Size)
        if int(m.MsgSize)+recSize > enums.GNS_MAX_BLOCK_SIZE {
@@ -117,7 +121,7 @@ func (m *GNSLookupResultMsg) AddRecord(rec 
*GNSResourceRecord) error {
        return nil
 }
 
-// String
+// String returns a human-readable representation of the message.
 func (m *GNSLookupResultMsg) String() string {
        return fmt.Sprintf("GNSLookupResultMsg{Id=%d,Count=%d}", m.Id, m.Count)
 }
diff --git a/src/gnunet/message/msg_namecache.go 
b/src/gnunet/message/msg_namecache.go
index 8f82622..9e3312d 100644
--- a/src/gnunet/message/msg_namecache.go
+++ b/src/gnunet/message/msg_namecache.go
@@ -33,7 +33,7 @@ func NewNamecacheLookupMsg(query *crypto.HashCode) 
*NamecacheLookupMsg {
        }
 }
 
-// String
+// String returns a human-readable representation of the message.
 func (m *NamecacheLookupMsg) String() string {
        return fmt.Sprintf("NamecacheLookupMsg{Id=%d,Query=%s}",
                m.Id, hex.EncodeToString(m.Query.Bits))
@@ -72,7 +72,7 @@ func NewNamecacheLookupResultMsg() *NamecacheLookupResultMsg {
        }
 }
 
-// String
+// String returns a human-readable representation of the message.
 func (m *NamecacheLookupResultMsg) String() string {
        return fmt.Sprintf("NamecacheLookupResultMsg{id=%d,expire=%s}",
                m.Id, m.Expire)
diff --git a/src/gnunet/message/msg_transport.go 
b/src/gnunet/message/msg_transport.go
index 1459d6a..54e63f2 100644
--- a/src/gnunet/message/msg_transport.go
+++ b/src/gnunet/message/msg_transport.go
@@ -4,22 +4,26 @@ import (
        "fmt"
        "time"
 
-       "github.com/bfix/gospel/crypto/ed25519"
-       "github.com/bfix/gospel/data"
+       "gnunet/crypto"
        "gnunet/enums"
        "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/data"
 )
 
 //----------------------------------------------------------------------
 // TRANSPORT_TCP_WELCOME
 //----------------------------------------------------------------------
 
+// TransportTcpWelcomeMsg
 type TransportTcpWelcomeMsg struct {
        MsgSize uint16       `order:"big"` // total size of message
        MsgType uint16       `order:"big"` // TRANSPORT_TCP_WELCOME (61)
        PeerID  *util.PeerID // Peer identity (EdDSA public key)
 }
 
+// NewTransportTcpWelcomeMsg creates a new message for a given peer.
 func NewTransportTcpWelcomeMsg(peerid *util.PeerID) *TransportTcpWelcomeMsg {
        if peerid == nil {
                peerid = util.NewPeerID(nil)
@@ -31,6 +35,7 @@ func NewTransportTcpWelcomeMsg(peerid *util.PeerID) 
*TransportTcpWelcomeMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *TransportTcpWelcomeMsg) String() string {
        return fmt.Sprintf("TransportTcpWelcomeMsg{peer=%s}", m.PeerID)
 }
@@ -40,6 +45,59 @@ func (msg *TransportTcpWelcomeMsg) Header() *MessageHeader {
        return &MessageHeader{msg.MsgSize, msg.MsgType}
 }
 
+//----------------------------------------------------------------------
+// TRANSPORT_PING
+//
+// Message used to ask a peer to validate receipt (to check an address
+// from a HELLO).  Followed by the address we are trying to validate,
+// or an empty address if we are just sending a PING to confirm that a
+// connection which the receiver (of the PING) initiated is still valid.
+//----------------------------------------------------------------------
+
+// TransportPingMsg
+type TransportPingMsg struct {
+       MsgSize   uint16       `order:"big"` // total size of message
+       MsgType   uint16       `order:"big"` // TRANSPORT_PING (372)
+       Challenge uint32       // Challenge code (to ensure fresh reply)
+       Target    *util.PeerID // EdDSA public key (long-term) of target peer
+       Address   []byte       `size:"*"` // encoded address
+}
+
+// TransportPingMsg creates a new message for given peer with an address to
+// be validated.
+func NewTransportPingMsg(target *util.PeerID, a *util.Address) 
*TransportPingMsg {
+       if target == nil {
+               target = util.NewPeerID(nil)
+       }
+       m := &TransportPingMsg{
+               MsgSize:   uint16(40),
+               MsgType:   TRANSPORT_PING,
+               Challenge: util.RndUInt32(),
+               Target:    target,
+               Address:   nil,
+       }
+       if a != nil {
+               if addrData, err := data.Marshal(a); err == nil {
+                       m.Address = addrData
+                       m.MsgSize += uint16(len(addrData))
+               }
+       }
+       return m
+}
+
+// String returns a human-readable representation of the message.
+func (m *TransportPingMsg) String() string {
+       a := new(util.Address)
+       data.Unmarshal(a, m.Address)
+       return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}",
+               m.Target, a, m.Challenge)
+}
+
+// Header returns the message header in a separate instance.
+func (msg *TransportPingMsg) Header() *MessageHeader {
+       return &MessageHeader{msg.MsgSize, msg.MsgType}
+}
+
 //----------------------------------------------------------------------
 // TRANSPORT_PONG
 //
@@ -53,29 +111,33 @@ func (msg *TransportTcpWelcomeMsg) Header() *MessageHeader 
{
 // a connection that we initiated).
 //----------------------------------------------------------------------
 
+// SignedAddress is the signed block of data representing a node address
 type SignedAddress struct {
-       SignLength uint32            `order:"big"` // Length of signed block
-       Purpose    uint32            `order:"big"` // SIG_TRANSPORT_PONG_OWN
-       ExpireOn   util.AbsoluteTime // usec epoch
-       AddrSize   uint32            `order:"big"`     // size of address
-       Address    []byte            `size:"AddrSize"` // address
+       Purpose  *crypto.SignaturePurpose // SIG_TRANSPORT_PONG_OWN
+       ExpireOn util.AbsoluteTime        // usec epoch
+       AddrSize uint32                   `order:"big"`     // size of address
+       Address  []byte                   `size:"AddrSize"` // address
 }
 
+// NewSignedAddress creates a new (signable) data block from an address.
 func NewSignedAddress(a *util.Address) *SignedAddress {
        // serialize address
        addrData, _ := data.Marshal(a)
        alen := len(addrData)
        addr := &SignedAddress{
-               SignLength: uint32(alen + 20),
-               Purpose:    enums.SIG_TRANSPORT_PONG_OWN,
-               ExpireOn:   util.AbsoluteTimeNow().Add(12 * time.Hour),
-               AddrSize:   uint32(alen),
-               Address:    make([]byte, alen),
+               Purpose: &crypto.SignaturePurpose{
+                       Size:    uint32(alen + 20),
+                       Purpose: enums.SIG_TRANSPORT_PONG_OWN,
+               },
+               ExpireOn: util.AbsoluteTimeNow().Add(12 * time.Hour),
+               AddrSize: uint32(alen),
+               Address:  make([]byte, alen),
        }
        copy(addr.Address, addrData)
        return addr
 }
 
+// TransportPongMsg
 type TransportPongMsg struct {
        MsgSize     uint16         `order:"big"` // total size of message
        MsgType     uint16         `order:"big"` // TRANSPORT_PING (372)
@@ -84,6 +146,8 @@ type TransportPongMsg struct {
        SignedBlock *SignedAddress // signed block of data
 }
 
+// NewTransportPongMsg creates a reponse message with an address the replying
+// peer wants to be reached.
 func NewTransportPongMsg(challenge uint32, a *util.Address) *TransportPongMsg {
        m := &TransportPongMsg{
                MsgSize:     72,
@@ -94,12 +158,13 @@ func NewTransportPongMsg(challenge uint32, a 
*util.Address) *TransportPongMsg {
        }
        if a != nil {
                sa := NewSignedAddress(a)
-               m.MsgSize += uint16(sa.SignLength)
+               m.MsgSize += uint16(sa.Purpose.Size)
                m.SignedBlock = sa
        }
        return m
 }
 
+// String returns a human-readable representation of the message.
 func (m *TransportPongMsg) String() string {
        a := new(util.Address)
        if err := data.Unmarshal(a, m.SignedBlock.Address); err == nil {
@@ -114,6 +179,7 @@ func (msg *TransportPongMsg) Header() *MessageHeader {
        return &MessageHeader{msg.MsgSize, msg.MsgType}
 }
 
+// Sign the address block of a pong message.
 func (m *TransportPongMsg) Sign(prv *ed25519.PrivateKey) error {
        data, err := data.Marshal(m.SignedBlock)
        if err != nil {
@@ -127,6 +193,7 @@ func (m *TransportPongMsg) Sign(prv *ed25519.PrivateKey) 
error {
        return nil
 }
 
+// Verify the address block of a pong message
 func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) (bool, error) {
        data, err := data.Marshal(m.SignedBlock)
        if err != nil {
@@ -139,55 +206,6 @@ func (m *TransportPongMsg) Verify(pub *ed25519.PublicKey) 
(bool, error) {
        return pub.EdVerify(data, sig)
 }
 
-//----------------------------------------------------------------------
-// TRANSPORT_PING
-//
-// Message used to ask a peer to validate receipt (to check an address
-// from a HELLO).  Followed by the address we are trying to validate,
-// or an empty address if we are just sending a PING to confirm that a
-// connection which the receiver (of the PING) initiated is still valid.
-//----------------------------------------------------------------------
-
-type TransportPingMsg struct {
-       MsgSize   uint16       `order:"big"` // total size of message
-       MsgType   uint16       `order:"big"` // TRANSPORT_PING (372)
-       Challenge uint32       // Challenge code (to ensure fresh reply)
-       Target    *util.PeerID // EdDSA public key (long-term) of target peer
-       Address   []byte       `size:"*"` // encoded address
-}
-
-func NewTransportPingMsg(target *util.PeerID, a *util.Address) 
*TransportPingMsg {
-       if target == nil {
-               target = util.NewPeerID(nil)
-       }
-       m := &TransportPingMsg{
-               MsgSize:   uint16(40),
-               MsgType:   TRANSPORT_PING,
-               Challenge: util.RndUInt32(),
-               Target:    target,
-               Address:   nil,
-       }
-       if a != nil {
-               if addrData, err := data.Marshal(a); err == nil {
-                       m.Address = addrData
-                       m.MsgSize += uint16(len(addrData))
-               }
-       }
-       return m
-}
-
-func (m *TransportPingMsg) String() string {
-       a := new(util.Address)
-       data.Unmarshal(a, m.Address)
-       return fmt.Sprintf("TransportPingMsg{target=%s,addr=%s,challenge=%d}",
-               m.Target, a, m.Challenge)
-}
-
-// Header returns the message header in a separate instance.
-func (msg *TransportPingMsg) Header() *MessageHeader {
-       return &MessageHeader{msg.MsgSize, msg.MsgType}
-}
-
 //----------------------------------------------------------------------
 // HELLO
 //
@@ -202,6 +220,7 @@ func (msg *TransportPingMsg) Header() *MessageHeader {
 // 4) address (address-length bytes)
 //----------------------------------------------------------------------
 
+// HelloAddress
 type HelloAddress struct {
        Transport string            // Name of transport
        AddrSize  uint16            `order:"big"` // Size of address entry
@@ -209,6 +228,7 @@ type HelloAddress struct {
        Address   []byte            `size:"AddrSize"` // Address specification
 }
 
+// NewHelloAddress create a new HELLO address from the given address
 func NewAddress(a *util.Address) *HelloAddress {
        addr := &HelloAddress{
                Transport: a.Transport,
@@ -220,11 +240,13 @@ func NewAddress(a *util.Address) *HelloAddress {
        return addr
 }
 
+// String returns a human-readable representation of the message.
 func (a *HelloAddress) String() string {
        return fmt.Sprintf("Address{%s,expire=%s}",
                util.AddressString(a.Transport, a.Address), a.ExpireOn)
 }
 
+// HelloMsg
 type HelloMsg struct {
        MsgSize    uint16          `order:"big"` // total size of message
        MsgType    uint16          `order:"big"` // HELLO (17)
@@ -233,6 +255,7 @@ type HelloMsg struct {
        Addresses  []*HelloAddress `size:"*"` // List of end-point addressess
 }
 
+// NewHelloMsg creates a new HELLO msg for a given peer.
 func NewHelloMsg(peerid *util.PeerID) *HelloMsg {
        if peerid == nil {
                peerid = util.NewPeerID(nil)
@@ -246,11 +269,13 @@ func NewHelloMsg(peerid *util.PeerID) *HelloMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *HelloMsg) String() string {
        return fmt.Sprintf("HelloMsg{peer=%s,friendsonly=%d,addr=%v}",
                m.PeerID, m.FriendOnly, m.Addresses)
 }
 
+// AddAddress adds a new address to the HELLO message.
 func (m *HelloMsg) AddAddress(a *HelloAddress) {
        m.Addresses = append(m.Addresses, a)
        m.MsgSize += uint16(len(a.Transport)) + a.AddrSize + 11
@@ -265,11 +290,13 @@ func (msg *HelloMsg) Header() *MessageHeader {
 // TRANSPORT_SESSION_ACK
 //----------------------------------------------------------------------
 
+// SessionAckMsg
 type SessionAckMsg struct {
        MsgSize uint16 `order:"big"` // total size of message
        MsgType uint16 `order:"big"` // TRANSPORT_SESSION_ACK (377)
 }
 
+// NewSessionAckMsg creates an new message (no body required).
 func NewSessionAckMsg() *SessionAckMsg {
        return &SessionAckMsg{
                MsgSize: 16,
@@ -277,6 +304,7 @@ func NewSessionAckMsg() *SessionAckMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *SessionAckMsg) String() string {
        return "SessionAck{}"
 }
@@ -290,6 +318,7 @@ func (msg *SessionAckMsg) Header() *MessageHeader {
 // TRANSPORT_SESSION_SYN
 //----------------------------------------------------------------------
 
+// SessionSynMsg
 type SessionSynMsg struct {
        MsgSize   uint16            `order:"big"` // total size of message
        MsgType   uint16            `order:"big"` // TRANSPORT_SESSION_SYN (375)
@@ -297,6 +326,7 @@ type SessionSynMsg struct {
        Timestamp util.AbsoluteTime // usec epoch
 }
 
+// NewSessionSynMsg creates a SYN request for the a session
 func NewSessionSynMsg() *SessionSynMsg {
        return &SessionSynMsg{
                MsgSize:   16,
@@ -306,6 +336,7 @@ func NewSessionSynMsg() *SessionSynMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *SessionSynMsg) String() string {
        return fmt.Sprintf("SessionSyn{timestamp=%s}", m.Timestamp)
 }
@@ -319,6 +350,7 @@ func (msg *SessionSynMsg) Header() *MessageHeader {
 // TRANSPORT_SESSION_SYN_ACK
 //----------------------------------------------------------------------
 
+// SessionSynAckMsg
 type SessionSynAckMsg struct {
        MsgSize   uint16            `order:"big"` // total size of message
        MsgType   uint16            `order:"big"` // TRANSPORT_SESSION_SYN_ACK 
(376)
@@ -326,6 +358,7 @@ type SessionSynAckMsg struct {
        Timestamp util.AbsoluteTime // usec epoch
 }
 
+// NewSessionSynAckMsg is an ACK for a SYN request
 func NewSessionSynAckMsg() *SessionSynAckMsg {
        return &SessionSynAckMsg{
                MsgSize:   16,
@@ -335,6 +368,7 @@ func NewSessionSynAckMsg() *SessionSynAckMsg {
        }
 }
 
+// String returns a human-readable representation of the message.
 func (m *SessionSynAckMsg) String() string {
        return fmt.Sprintf("SessionSynAck{timestamp=%s}", m.Timestamp)
 }
@@ -348,12 +382,14 @@ func (msg *SessionSynAckMsg) Header() *MessageHeader {
 // TRANSPORT_SESSION_QUOTA
 //----------------------------------------------------------------------
 
+// SessionQuotaMsg
 type SessionQuotaMsg struct {
        MsgSize uint16 `order:"big"` // total size of message
        MsgType uint16 `order:"big"` // TRANSPORT_SESSION_QUOTA (379)
        Quota   uint32 `order:"big"` // Quota in bytes per second
 }
 
+// NewSessionQuotaMsg announces a session quota to the other end of the 
session.
 func NewSessionQuotaMsg(quota uint32) *SessionQuotaMsg {
        m := new(SessionQuotaMsg)
        if quota > 0 {
@@ -364,6 +400,7 @@ func NewSessionQuotaMsg(quota uint32) *SessionQuotaMsg {
        return m
 }
 
+// String returns a human-readable representation of the message.
 func (m *SessionQuotaMsg) String() string {
        return fmt.Sprintf("SessionQuotaMsg{%sB/s}", 
util.Scale1024(uint64(m.Quota)))
 }
@@ -374,57 +411,63 @@ func (msg *SessionQuotaMsg) Header() *MessageHeader {
 }
 
 //----------------------------------------------------------------------
-// TRANSPORT_SESSION_KEEPALIVE_RESPONSE
+// TRANSPORT_SESSION_KEEPALIVE
 //----------------------------------------------------------------------
 
-type SessionKeepAliveRespMsg struct {
+// SessionKeepAliveMsg
+type SessionKeepAliveMsg struct {
        MsgSize uint16 `order:"big"` // total size of message
-       MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE_RESPONSE 
(382)
+       MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE (381)
        Nonce   uint32
 }
 
-func NewSessionKeepAliveRespMsg(nonce uint32) *SessionKeepAliveRespMsg {
-       m := &SessionKeepAliveRespMsg{
+// NewSessionKeepAliveMsg creates a new request to keep a session.
+func NewSessionKeepAliveMsg() *SessionKeepAliveMsg {
+       m := &SessionKeepAliveMsg{
                MsgSize: 8,
-               MsgType: TRANSPORT_SESSION_KEEPALIVE_RESPONSE,
-               Nonce:   nonce,
+               MsgType: TRANSPORT_SESSION_KEEPALIVE,
+               Nonce:   util.RndUInt32(),
        }
        return m
 }
 
-func (m *SessionKeepAliveRespMsg) String() string {
-       return fmt.Sprintf("SessionKeepAliveRespMsg{%d}", m.Nonce)
+// String returns a human-readable representation of the message.
+func (m *SessionKeepAliveMsg) String() string {
+       return fmt.Sprintf("SessionKeepAliveMsg{%d}", m.Nonce)
 }
 
 // Header returns the message header in a separate instance.
-func (msg *SessionKeepAliveRespMsg) Header() *MessageHeader {
+func (msg *SessionKeepAliveMsg) Header() *MessageHeader {
        return &MessageHeader{msg.MsgSize, msg.MsgType}
 }
 
 //----------------------------------------------------------------------
-// TRANSPORT_SESSION_KEEPALIVE
+// TRANSPORT_SESSION_KEEPALIVE_RESPONSE
 //----------------------------------------------------------------------
 
-type SessionKeepAliveMsg struct {
+// SessionKeepAliveRespMsg
+type SessionKeepAliveRespMsg struct {
        MsgSize uint16 `order:"big"` // total size of message
-       MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE (381)
+       MsgType uint16 `order:"big"` // TRANSPORT_SESSION_KEEPALIVE_RESPONSE 
(382)
        Nonce   uint32
 }
 
-func NewSessionKeepAliveMsg() *SessionKeepAliveMsg {
-       m := &SessionKeepAliveMsg{
+// NewSessionKeepAliveRespMsg is a response message for a "keep session" 
request.
+func NewSessionKeepAliveRespMsg(nonce uint32) *SessionKeepAliveRespMsg {
+       m := &SessionKeepAliveRespMsg{
                MsgSize: 8,
-               MsgType: TRANSPORT_SESSION_KEEPALIVE,
-               Nonce:   util.RndUInt32(),
+               MsgType: TRANSPORT_SESSION_KEEPALIVE_RESPONSE,
+               Nonce:   nonce,
        }
        return m
 }
 
-func (m *SessionKeepAliveMsg) String() string {
-       return fmt.Sprintf("SessionKeepAliveMsg{%d}", m.Nonce)
+// String returns a human-readable representation of the message.
+func (m *SessionKeepAliveRespMsg) String() string {
+       return fmt.Sprintf("SessionKeepAliveRespMsg{%d}", m.Nonce)
 }
 
 // Header returns the message header in a separate instance.
-func (msg *SessionKeepAliveMsg) Header() *MessageHeader {
+func (msg *SessionKeepAliveRespMsg) Header() *MessageHeader {
        return &MessageHeader{msg.MsgSize, msg.MsgType}
 }
diff --git a/src/gnunet/service/client.go b/src/gnunet/service/client.go
index 2d1c3dd..3bae7da 100644
--- a/src/gnunet/service/client.go
+++ b/src/gnunet/service/client.go
@@ -1,9 +1,10 @@
 package service
 
 import (
-       "github.com/bfix/gospel/logger"
        "gnunet/message"
        "gnunet/transport"
+
+       "github.com/bfix/gospel/logger"
 )
 
 // Client
diff --git a/src/gnunet/service/gns/block.go b/src/gnunet/service/gns/block.go
new file mode 100644
index 0000000..9d83f99
--- /dev/null
+++ b/src/gnunet/service/gns/block.go
@@ -0,0 +1,185 @@
+package gns
+
+import (
+       "fmt"
+
+       "gnunet/crypto"
+       "gnunet/enums"
+       "gnunet/message"
+       "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/data"
+)
+
+var (
+       ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted")
+)
+
+//======================================================================
+// GNS block: An encrypted and signed container for GNS resource records
+// that represents the "atomic" data structure associated with a GNS
+// label in a given zone.
+//======================================================================
+
+// SignedBlockData: signed and encrypted list of resource records stored
+// in a GNSRecordSet
+type SignedBlockData struct {
+       Purpose *crypto.SignaturePurpose // Size and purpose of signature (8 
bytes)
+       Expire  util.AbsoluteTime        // Expiration time of the block.
+       EncData []byte                   `size:"*"` // encrypted GNSRecordSet
+
+       // transient data (not serialized)
+       data []byte // unencrypted GNSRecord set
+}
+
+// GNSBlock is the result of GNS lookups for a given label in a zone.
+type GNSBlock struct {
+       Signature  []byte `size:"64"` // Signature of the block.
+       DerivedKey []byte `size:"32"` // Derived key used for signing
+       Block      *SignedBlockData
+
+       // transient data (not serialized)
+       checked   bool // block integrity checked
+       verified  bool // block signature verified (internal)
+       decrypted bool // block data decrypted (internal)
+}
+
+// String returns the human-readable representation of a GNSBlock
+func (b *GNSBlock) String() string {
+       return fmt.Sprintf("GNSBlock{Verified=%v,Decrypted=%v,data=[%d]}",
+               b.verified, b.decrypted, len(b.Block.EncData))
+}
+
+// Records returns the list of resource records in a block.
+func (b *GNSBlock) Records() ([]*message.GNSResourceRecord, error) {
+       // check if block is decrypted
+       if !b.decrypted {
+               return nil, ErrBlockNotDecrypted
+       }
+       // parse block data into record set
+       rs := NewGNSRecordSet()
+       if err := data.Unmarshal(rs, b.Block.data); err != nil {
+               return nil, err
+       }
+       return rs.Records, nil
+}
+
+// Verify the integrity of the block data from a signature.
+func (b *GNSBlock) Verify(zoneKey *ed25519.PublicKey, label string) (err 
error) {
+       // Integrity check performed
+       b.checked = true
+
+       // verify derived key
+       dkey := ed25519.NewPublicKeyFromBytes(b.DerivedKey)
+       dkey2 := crypto.DerivePublicKey(zoneKey, label, "gns")
+       if !dkey.Q.Equals(dkey2.Q) {
+               return fmt.Errorf("Invalid signature key for GNS Block")
+       }
+       // verify signature
+       var (
+               sig *ed25519.EcSignature
+               buf []byte
+               ok  bool
+       )
+       if sig, err = ed25519.NewEcSignatureFromBytes(b.Signature); err != nil {
+               return
+       }
+       if buf, err = data.Marshal(b.Block); err != nil {
+               return
+       }
+       if ok, err = dkey.EcVerify(buf, sig); err == nil && !ok {
+               err = fmt.Errorf("Signature verification failed for GNS block")
+       }
+       b.verified = true
+       return
+}
+
+// Decrypt block data with a key/iv combination derived from (PKEY,label)
+func (b *GNSBlock) Decrypt(zoneKey *ed25519.PublicKey, label string) (err 
error) {
+       // decrypt payload
+       b.Block.data, err = crypto.DecryptBlock(b.Block.EncData, zoneKey, label)
+       b.decrypted = true
+       return
+}
+
+// NewGNSBlock instantiates an empty GNS block
+func NewGNSBlock() *GNSBlock {
+       return &GNSBlock{
+               Signature:  make([]byte, 64),
+               DerivedKey: make([]byte, 32),
+               Block: &SignedBlockData{
+                       Purpose: new(crypto.SignaturePurpose),
+                       Expire:  *new(util.AbsoluteTime),
+                       EncData: nil,
+                       data:    nil,
+               },
+               checked:   false,
+               verified:  false,
+               decrypted: false,
+       }
+}
+
+//----------------------------------------------------------------------
+// GNSRecordSet
+//----------------------------------------------------------------------
+
+// GNSRecordSet ist the GNUnet data structure for a list of resource records
+// in a GNSBlock. As part of GNUnet messages, the record set is padded so that
+// the binary size of (records||padding) is the smallest power of two.
+type GNSRecordSet struct {
+       Count   uint32                       `order:"big"`  // number of 
resource records
+       Records []*message.GNSResourceRecord `size:"Count"` // list of resource 
records
+       Padding []byte                       `size:"*"`     // padding
+}
+
+// NewGNSRecordSet returns an empty resource record set.
+func NewGNSRecordSet() *GNSRecordSet {
+       return &GNSRecordSet{
+               Count:   0,
+               Records: make([]*message.GNSResourceRecord, 0),
+               Padding: make([]byte, 0),
+       }
+}
+
+// AddRecord to append a resource record to the set.
+func (rs *GNSRecordSet) AddRecord(rec *message.GNSResourceRecord) {
+       rs.Count++
+       rs.Records = append(rs.Records, rec)
+}
+
+//======================================================================
+// List of resource records types (for GNS/DNS queries)
+//======================================================================
+
+// RRTypeList is a list of integers representing RR types.
+type RRTypeList []int
+
+// Initialize a new type list with given type values
+func NewRRTypeList(args ...int) (res RRTypeList) {
+       for _, val := range args {
+               // if GNS_TYPE_ANY is encountered, it becomes the sole type
+               if val == enums.GNS_TYPE_ANY {
+                       res = make(RRTypeList, 1)
+                       res[0] = val
+                       return
+               }
+               res = append(res, val)
+       }
+       return
+}
+
+// HasType returns true if the type is included in the list
+func (tl RRTypeList) HasType(t int) bool {
+       // return true if type is GNS_TYPE_ANY
+       if tl[0] == enums.GNS_TYPE_ANY {
+               return true
+       }
+       // check for type in list
+       for _, val := range tl {
+               if val == t {
+                       return true
+               }
+       }
+       return false
+}
diff --git a/src/gnunet/service/gns/block_handler.go 
b/src/gnunet/service/gns/block_handler.go
new file mode 100644
index 0000000..860b4a7
--- /dev/null
+++ b/src/gnunet/service/gns/block_handler.go
@@ -0,0 +1,398 @@
+package gns
+
+import (
+       "encoding/hex"
+       "fmt"
+
+       "gnunet/enums"
+       "gnunet/message"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/logger"
+)
+
+// HdlrInst is the type for functions that instanciate custom block handlers.
+type HdlrInst func(*message.GNSResourceRecord, []string) (BlockHandler, error)
+
+// Error codes
+var (
+       ErrInvalidRecordMix = fmt.Errorf("Invalid mix of RR types in block")
+       ErrBlockHandler     = fmt.Errorf("Internal block handler failure")
+)
+
+// Mapping of RR types to BlockHandler instanciation functions
+var (
+       customHandler = map[int]HdlrInst{
+               enums.GNS_TYPE_PKEY:    NewPkeyHandler,
+               enums.GNS_TYPE_GNS2DNS: NewGns2DnsHandler,
+               enums.GNS_TYPE_BOX:     NewBoxHandler,
+               enums.GNS_TYPE_LEHO:    NewLehoHandler,
+       }
+)
+
+//======================================================================
+// GNS blocks that contain special records (PKEY, GNS2DNS, BOX, LEHO...)
+// require special treatment with respect to other resource records with
+// different types in the same block. Usually only certain other types
+// (or none at all) are allowed.
+//======================================================================
+
+// BlockHandler interface.
+type BlockHandler interface {
+       // AddRecord inserts a RR into the BlockHandler for (later) processing.
+       // The handler can inspect the remaining labels in a path if required.
+       // It returns an error if a record is not accepted by the block handler.
+       AddRecord(rr *message.GNSResourceRecord, labels []string) error
+
+       // TypeAction returns a flag indicating how a resource record of a
+       // given type is to be treated by a custom block handler:
+       //   = -1: Record is not allowed
+       //   =  0: Record is allowed but will be ignored
+       //   =  1: Record is allowed and will be processed
+       TypeAction(t int) int
+
+       // Records returns a list of RR of the given types associated with
+       // the custom handler
+       Records(kind RRTypeList) *GNSRecordSet
+}
+
+//----------------------------------------------------------------------
+// Manage list of block handlers
+// Under normal circumstances there is only one (or none) block handler
+// per block, but future constructs may allow multiple block handlers
+// to be present. The block handler list implements the BlockHandler
+// interface.
+// The BlockHandlerList maintains a map of actually instantiated handlers
+// (indexed by record type) and a list of record types (with occurrence
+// count) in the block.
+//----------------------------------------------------------------------
+
+// BlockHandlerList is a list of block handlers instantiated.
+type BlockHandlerList struct {
+       list map[int]BlockHandler // list of handler instances
+}
+
+// NewBlockHandlerList instantiates an a list of active block handlers
+// for a given set of records (GNS block).
+func NewBlockHandlerList(records []*message.GNSResourceRecord, labels 
[]string) (*BlockHandlerList, error) {
+       // initialize block handler list
+       hl := &BlockHandlerList{
+               list: make(map[int]BlockHandler),
+       }
+       // build a list of record types that are handled by a custom handler.
+       rrList := NewRRTypeList(
+               enums.GNS_TYPE_PKEY,
+               enums.GNS_TYPE_GNS2DNS,
+               enums.GNS_TYPE_BOX,
+               enums.GNS_TYPE_LEHO)
+
+       // Traverse record list and build list of handler instances
+       for _, rec := range records {
+               // check for custom handler type
+               rrType := int(rec.Type)
+               if rrList.HasType(rrType) {
+                       // check if a handler for given type already exists
+                       var (
+                               hdlr BlockHandler
+                               ok   bool
+                               err  error
+                       )
+                       if hdlr, ok = hl.list[rrType]; ok {
+                               // add record to existing handler
+                               if err = hdlr.AddRecord(rec, labels); err != 
nil {
+                                       return nil, err
+                               }
+                               continue
+                       }
+                       // create a new handler instance
+                       switch rrType {
+                       case enums.GNS_TYPE_PKEY:
+                               hdlr, err = NewPkeyHandler(rec, labels)
+                       case enums.GNS_TYPE_GNS2DNS:
+                               hdlr, err = NewGns2DnsHandler(rec, labels)
+                       case enums.GNS_TYPE_BOX:
+                               hdlr, err = NewBoxHandler(rec, labels)
+                       case enums.GNS_TYPE_LEHO:
+                               hdlr, err = NewLehoHandler(rec, labels)
+                       }
+                       if err != nil {
+                               return nil, err
+                       }
+                       // store handler in list
+                       hl.list[rrType] = hdlr
+               }
+       }
+       return hl, nil
+}
+
+// GetHandler returns a BlockHandler for the given key. If no block handler 
exists
+// under the given name, a new one is created and stored in the list. The type 
of
+// the new block handler is derived from the key value.
+func (hl *BlockHandlerList) GetHandler(t int) BlockHandler {
+       // return handler for given key if it exists
+       if hdlr, ok := hl.list[t]; ok {
+               return hdlr
+       }
+       return nil
+}
+
+//----------------------------------------------------------------------
+// PKEY handler: Only one PKEY as sole record in a block
+//----------------------------------------------------------------------
+
+// PkeyHandler implementing the BlockHandler interface
+type PkeyHandler struct {
+       pkey *ed25519.PublicKey         // Zone key
+       rec  *message.GNSResourceRecord // associated recource record
+}
+
+// NewPkeyHandler returns a new BlockHandler instance
+func NewPkeyHandler(rec *message.GNSResourceRecord, labels []string) 
(BlockHandler, error) {
+       if int(rec.Type) != enums.GNS_TYPE_PKEY {
+               return nil, ErrInvalidRecordType
+       }
+       h := &PkeyHandler{
+               pkey: nil,
+       }
+       if err := h.AddRecord(rec, labels); err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// AddRecord inserts a PKEY record into the handler.
+func (h *PkeyHandler) AddRecord(rec *message.GNSResourceRecord, labels 
[]string) error {
+       if int(rec.Type) != enums.GNS_TYPE_PKEY {
+               return ErrInvalidRecordType
+       }
+       // check for sole PKEY record in block
+       if h.pkey != nil {
+               return ErrInvalidPKEY
+       }
+       // check for sane key data
+       if len(rec.Data) != 32 {
+               return ErrInvalidPKEY
+       }
+       // set a PKEY handler
+       h.pkey = ed25519.NewPublicKeyFromBytes(rec.Data)
+       h.rec = rec
+       return nil
+}
+
+// TypeAction return a flag indicating how a resource record of a given type
+// is to be treated (see BlockHandler interface)
+func (h *PkeyHandler) TypeAction(t int) int {
+       // no other resource record type is not allowed
+       if t == enums.GNS_TYPE_PKEY {
+               return 1
+       }
+       return -1
+}
+
+// Records returns a list of RR of the given type associated with this handler
+func (h *PkeyHandler) Records(kind RRTypeList) *GNSRecordSet {
+       rs := NewGNSRecordSet()
+       if kind.HasType(enums.GNS_TYPE_PKEY) {
+               rs.AddRecord(h.rec)
+       }
+       return rs
+}
+
+//----------------------------------------------------------------------
+// GNS2DNS handler
+//----------------------------------------------------------------------
+
+// Gns2DnsHandler implementing the BlockHandler interface
+type Gns2DnsHandler struct {
+       Name    string                       // DNS query name
+       Servers []string                     // DNS servers to ask
+       recs    []*message.GNSResourceRecord // list of rersource records
+}
+
+// NewGns2DnsHandler returns a new BlockHandler instance
+func NewGns2DnsHandler(rec *message.GNSResourceRecord, labels []string) 
(BlockHandler, error) {
+       if int(rec.Type) != enums.GNS_TYPE_GNS2DNS {
+               return nil, ErrInvalidRecordType
+       }
+       h := &Gns2DnsHandler{
+               Name:    "",
+               Servers: make([]string, 0),
+               recs:    make([]*message.GNSResourceRecord, 0),
+       }
+       if err := h.AddRecord(rec, labels); err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// AddRecord inserts a GNS2DNS record into the handler.
+func (h *Gns2DnsHandler) AddRecord(rec *message.GNSResourceRecord, labels 
[]string) error {
+       if int(rec.Type) != enums.GNS_TYPE_GNS2DNS {
+               return ErrInvalidRecordType
+       }
+       logger.Printf(logger.DBG, "[gns] GNS2DNS data: %s\n", 
hex.EncodeToString(rec.Data))
+
+       // extract list of names in DATA block:
+       next, dnsQuery := DNSNameFromBytes(rec.Data, 0)
+       dnsServer := string(rec.Data[next : len(rec.Data)-1])
+       logger.Printf(logger.DBG, "[gns] GNS2DNS query '%s'@'%s'\n", dnsQuery, 
dnsServer)
+       if len(dnsServer) == 0 || len(dnsQuery) == 0 {
+               return ErrInvalidRecordBody
+       }
+
+       // check if all GNS2DNS records refer to the same query name
+       if len(h.Servers) == 0 {
+               h.Name = dnsQuery
+       }
+       if dnsQuery != h.Name {
+               return ErrInvalidRecordBody
+       }
+       h.Servers = append(h.Servers, dnsServer)
+       h.recs = append(h.recs, rec)
+       return nil
+}
+
+// TypeAction return a flag indicating how a resource record of a given type
+// is to be treated (see BlockHandler interface)
+func (h *Gns2DnsHandler) TypeAction(t int) int {
+       // anything goes...
+       return 1
+}
+
+// Records returns a list of RR of the given type associated with this handler
+func (h *Gns2DnsHandler) Records(kind RRTypeList) *GNSRecordSet {
+       rs := NewGNSRecordSet()
+       if kind.HasType(enums.GNS_TYPE_GNS2DNS) {
+               for _, rec := range h.recs {
+                       rs.AddRecord(rec)
+               }
+       }
+       return rs
+}
+
+//----------------------------------------------------------------------
+// BOX handler
+//----------------------------------------------------------------------
+
+// BoxHandler implementing the BlockHandler interface
+type BoxHandler struct {
+       boxes map[string]*Box // map of found boxes
+}
+
+// NewBoxHandler returns a new BlockHandler instance
+func NewBoxHandler(rec *message.GNSResourceRecord, labels []string) 
(BlockHandler, error) {
+       if int(rec.Type) != enums.GNS_TYPE_BOX {
+               return nil, ErrInvalidRecordType
+       }
+       h := &BoxHandler{
+               boxes: make(map[string]*Box),
+       }
+       if err := h.AddRecord(rec, labels); err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// AddRecord inserts a BOX record into the handler.
+func (h *BoxHandler) AddRecord(rec *message.GNSResourceRecord, labels 
[]string) error {
+       if int(rec.Type) != enums.GNS_TYPE_BOX {
+               return ErrInvalidRecordType
+       }
+       logger.Printf(logger.DBG, "[box-rr] for labels %v\n", labels)
+       // check if we need to process the BOX record:
+       // (1) only two remaining labels
+       if len(labels) != 2 {
+               return nil
+       }
+       // (2) remaining labels must start with '_'
+       if labels[0][0] != '_' || labels[1][0] != '_' {
+               return nil
+       }
+       // (3) check of "svc" and "proto" match values in the BOX
+       box := NewBox(rec)
+       if box.Matches(labels) {
+               logger.Println(logger.DBG, "[box-rr] MATCH -- adding record")
+               h.boxes[box.key] = box
+       }
+       return nil
+}
+
+// TypeAction return a flag indicating how a resource record of a given type
+// is to be treated (see BlockHandler interface)
+func (h *BoxHandler) TypeAction(t int) int {
+       // anything goes...
+       return 1
+}
+
+// Records returns a list of RR of the given type associated with this handler
+func (h *BoxHandler) Records(kind RRTypeList) *GNSRecordSet {
+       rs := NewGNSRecordSet()
+       for _, box := range h.boxes {
+               if kind.HasType(int(box.Type)) {
+                       // valid box found: assemble new resource record.
+                       rr := new(message.GNSResourceRecord)
+                       rr.Expires = box.rec.Expires
+                       rr.Flags = box.rec.Flags
+                       rr.Type = box.Type
+                       rr.Size = uint32(len(box.RR))
+                       rr.Data = box.RR
+                       rs.AddRecord(rr)
+               }
+       }
+       return rs
+}
+
+//----------------------------------------------------------------------
+// LEHO handler
+//----------------------------------------------------------------------
+
+// LehoHandler implementing the BlockHandler interface
+type LehoHandler struct {
+       name string
+       rec  *message.GNSResourceRecord
+}
+
+// NewLehoHandler returns a new BlockHandler instance
+func NewLehoHandler(rec *message.GNSResourceRecord, labels []string) 
(BlockHandler, error) {
+       if int(rec.Type) != enums.GNS_TYPE_LEHO {
+               return nil, ErrInvalidRecordType
+       }
+       h := &LehoHandler{
+               name: "",
+       }
+       if err := h.AddRecord(rec, labels); err != nil {
+               return nil, err
+       }
+       return h, nil
+}
+
+// AddRecord inserts a LEHO record into the handler.
+func (h *LehoHandler) AddRecord(rec *message.GNSResourceRecord, labels 
[]string) error {
+       if int(rec.Type) != enums.GNS_TYPE_LEHO {
+               return ErrInvalidRecordType
+       }
+       h.name = string(rec.Data)
+       h.rec = rec
+       return nil
+}
+
+// TypeAction return a flag indicating how a resource record of a given type
+// is to be treated (see BlockHandler interface)
+func (h *LehoHandler) TypeAction(t int) int {
+       // only A and AAAA records allowed beside LEHO
+       switch t {
+       case enums.GNS_TYPE_LEHO, enums.GNS_TYPE_DNS_A, enums.GNS_TYPE_DNS_AAAA:
+               return 1
+       default:
+               return -1
+       }
+}
+
+// Records returns a list of RR of the given type associated with this handler
+func (h *LehoHandler) Records(kind RRTypeList) *GNSRecordSet {
+       rs := NewGNSRecordSet()
+       if kind.HasType(enums.GNS_TYPE_LEHO) {
+               rs.AddRecord(h.rec)
+       }
+       return rs
+}
diff --git a/src/gnunet/service/gns/box.go b/src/gnunet/service/gns/box.go
new file mode 100644
index 0000000..e2d3609
--- /dev/null
+++ b/src/gnunet/service/gns/box.go
@@ -0,0 +1,151 @@
+package gns
+
+import (
+       "encoding/hex"
+       "strconv"
+       "strings"
+
+       "gnunet/message"
+
+       "github.com/bfix/gospel/data"
+       "github.com/bfix/gospel/logger"
+)
+
+// Box is an encapsulated RR for special names
+type Box struct {
+       Proto uint16 `order:"big"` // Protcol identifier
+       Svc   uint16 `order:"big"` // Service identifier
+       Type  uint32 `order:"big"` // Type of embedded RR
+       RR    []byte `size:"*"`    // embedded RR
+
+       // transient attributes (not serialized)
+       key string                     // map key for box instance
+       rec *message.GNSResourceRecord // originating RR
+}
+
+// NewBox creates a new box instance from a BOX resource record.
+func NewBox(rec *message.GNSResourceRecord) *Box {
+       b := new(Box)
+       if err := data.Unmarshal(b, rec.Data); err != nil {
+               logger.Printf(logger.ERROR, "[gns] Can't unmarshal BOX")
+               return nil
+       }
+       b.key = hex.EncodeToString(rec.Data[:8])
+       b.rec = rec
+       return b
+}
+
+// Matches verifies that the remaining labels comply with the values
+// in the BOX record.
+func (b *Box) Matches(labels []string) bool {
+       // resolve protocol and service names
+       proto, protoName := GetProtocol(labels[0])
+       svc, _ := GetService(labels[1], protoName)
+       // no match on invalid resolution
+       if proto == 0 || svc == 0 {
+               return false
+       }
+       // check for matching values in box
+       return proto == b.Proto && svc == b.Svc
+}
+
+//----------------------------------------------------------------------
+// helper functions
+
+// list of handled protocols in BOX records
+var protocols = map[string]int{
+       "icmp":      1,
+       "igmp":      2,
+       "tcp":       6,
+       "udp":       17,
+       "ipv6-icmp": 58,
+}
+
+// GetProtocol returns the protocol number and name for a given name. The
+// name can be  an integer value (e.g. "_6" for "tcp") or a mnemonic name
+// (e.g. like "_tcp").
+func GetProtocol(name string) (uint16, string) {
+       // check for required prefix
+       if name[0] != '_' {
+               return 0, ""
+       }
+       name = strings.ToLower(name[1:])
+
+       // if label is an integer value it is the protocol number
+       if val, err := strconv.Atoi(name); err == nil {
+               // check for valid number (reverse protocol lookup)
+               for label, id := range protocols {
+                       if id == val {
+                               // return found entry
+                               return uint16(val), label
+                       }
+               }
+               // number out of range
+               return 0, ""
+       }
+       // try to resolve via protocol map
+       if id, ok := protocols[name]; ok {
+               return uint16(id), name
+       }
+       // resolution failed
+       return 0, ""
+}
+
+// list of services (per protocol) handled in BOX records
+var services = map[string]map[string]int{
+       "udp": {
+               "domain": 53,
+       },
+       "tcp": {
+               "ftp":    21,
+               "ftps":   990,
+               "gopher": 70,
+               "http":   80,
+               "https":  443,
+               "imap2":  143,
+               "imap3":  220,
+               "imaps":  993,
+               "pop3":   110,
+               "pop3s":  995,
+               "smtp":   25,
+               "ssh":    22,
+               "telnet": 23,
+       },
+}
+
+// GetService returns the port number and the name of a service (with given
+// protocol).  The name can be an integer value (e.g. "_443" for "https") or
+// a mnemonic name (e.g. like "_https").
+func GetService(name, proto string) (uint16, string) {
+       // check for required prefix
+       if name[0] != '_' {
+               return 0, ""
+       }
+       name = strings.ToLower(name[1:])
+
+       // get list of services for given protocol
+       svcs, ok := services[proto]
+       if !ok {
+               // no services available for this protocol
+               return 0, ""
+       }
+
+       // if label is an integer value it is the port number
+       if val, err := strconv.Atoi(name); err == nil {
+               // check for valid number (reverse service lookup)
+               for label, id := range svcs {
+                       if id == val {
+                               // return found entry
+                               return uint16(val), label
+                       }
+               }
+               // number out of range
+               return 0, ""
+       }
+       // try to resolve via services map
+       if id, ok := svcs[name]; ok {
+               return uint16(id), name
+       }
+       // resolution failed
+       return 0, ""
+}
diff --git a/src/gnunet/service/gns/dns.go b/src/gnunet/service/gns/dns.go
index 2ebe331..fe138b0 100644
--- a/src/gnunet/service/gns/dns.go
+++ b/src/gnunet/service/gns/dns.go
@@ -47,7 +47,7 @@ func DNSNameFromBytes(b []byte, offset int) (int, string) {
 
 // queryDNS resolves a name on a given nameserver and delivers all matching
 // resource record (of type 'kind') to the result channel.
-func queryDNS(id int, name string, server net.IP, kind int, res chan 
*GNSRecordSet) {
+func queryDNS(id int, name string, server net.IP, kind RRTypeList, res chan 
*GNSRecordSet) {
        logger.Printf(logger.DBG, "[dns][%d] Starting query for '%s' on 
'%s'...\n", id, name, server.String())
 
        // assemble query
@@ -91,27 +91,32 @@ func queryDNS(id int, name string, server net.IP, kind int, 
res chan *GNSRecordS
                }
                set := NewGNSRecordSet()
                for _, record := range in.Answer {
-                       // create a new GNS resource record
-                       rr := new(message.GNSResourceRecord)
-                       rr.Expires = util.AbsoluteTimeNever()
-                       rr.Flags = 0
-                       rr.Type = uint32(record.Header().Rrtype)
-                       rr.Size = uint32(record.Header().Rdlength)
-                       rr.Data = make([]byte, rr.Size)
+                       // check if answer record is of requested type
+                       if kind.HasType(int(record.Header().Rrtype)) {
+                               // get wire-format of resource record
+                               buf := make([]byte, 2048)
+                               n, err := dns.PackRR(record, buf, 0, nil, false)
+                               if err != nil {
+                                       logger.Printf(logger.WARN, "[dns][%d] 
Failed to get RR data for %s\n", id, err.Error())
+                                       continue
+                               }
 
-                       // get wire-format of resource record
-                       buf := make([]byte, 2048)
-                       n, err := dns.PackRR(record, buf, 0, nil, false)
-                       if err != nil {
-                               logger.Printf(logger.WARN, "[dns][%d] Failed to 
get RR data for %s\n", id, err.Error())
-                               continue
-                       }
-                       if n < int(rr.Size) {
-                               logger.Printf(logger.WARN, "[dns][%d] Nit 
enough data in RR (%d != %d)\n", id, n, rr.Size)
-                               continue
+                               // create a new GNS resource record
+                               rr := new(message.GNSResourceRecord)
+                               expires := 
time.Now().Add(time.Duration(record.Header().Ttl) * time.Second)
+                               rr.Expires = util.NewAbsoluteTime(expires)
+                               rr.Flags = 0
+                               rr.Type = uint32(record.Header().Rrtype)
+                               rr.Size = uint32(record.Header().Rdlength)
+                               rr.Data = make([]byte, rr.Size)
+
+                               if n < int(rr.Size) {
+                                       logger.Printf(logger.WARN, "[dns][%d] 
Not enough data in RR (%d != %d)\n", id, n, rr.Size)
+                                       continue
+                               }
+                               copy(rr.Data, buf[n-int(rr.Size):])
+                               set.AddRecord(rr)
                        }
-                       copy(rr.Data, buf[n-int(rr.Size):])
-                       set.AddRecord(rr)
                }
                logger.Printf(logger.WARN, "[dns][%d] %d resource records 
extracted from response (%d/5).\n", id, set.Count, retry+1)
                res <- set
@@ -124,7 +129,7 @@ func queryDNS(id int, name string, server net.IP, kind int, 
res chan *GNSRecordS
 // ResolveDNS resolves a name in DNS. Multiple DNS servers are queried in
 // parallel; the first result delivered by any of the servers is returned
 // as the result list of matching resource records.
-func (gns *GNSModule) ResolveDNS(name string, servers []string, kind int, pkey 
*ed25519.PublicKey) (set *GNSRecordSet, err error) {
+func (gns *GNSModule) ResolveDNS(name string, servers []string, kind 
RRTypeList, pkey *ed25519.PublicKey) (set *GNSRecordSet, err error) {
        logger.Printf(logger.DBG, "[dns] Resolution of '%s' starting...\n", 
name)
 
        // start DNS queries concurrently
@@ -133,20 +138,22 @@ func (gns *GNSModule) ResolveDNS(name string, servers 
[]string, kind int, pkey *
        for idx, srv := range servers {
                // check if srv is an IPv4/IPv6 address
                addr := net.ParseIP(srv)
+               logger.Printf(logger.DBG, "ParseIP('%s', len=%d) --> %v\n", 
srv, len(srv), addr)
                if addr == nil {
+                       query := NewRRTypeList(enums.GNS_TYPE_DNS_A, 
enums.GNS_TYPE_DNS_AAAA)
                        // no; resolve server name in GNS
                        if strings.HasSuffix(srv, ".+") {
                                // resolve server name relative to current zone
                                zone := util.EncodeBinaryToString(pkey.Bytes())
                                srv = strings.TrimSuffix(srv, ".+")
-                               set, err = gns.Resolve(srv, pkey, 
enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
+                               set, err = gns.Resolve(srv, pkey, query, 
enums.GNS_LO_DEFAULT)
                                if err != nil {
                                        logger.Printf(logger.ERROR, "[dns] 
Can't resolve NS server '%s' in '%s'\n", srv, zone)
                                        continue
                                }
                        } else {
-                               // resolve absolute GNS name (MUST end in a 
PKEY)
-                               set, err = gns.Resolve(srv, nil, 
enums.GNS_TYPE_ANY, enums.GNS_LO_DEFAULT)
+                               // resolve absolute GNS name (name MUST end in 
a PKEY)
+                               set, err = gns.Resolve(srv, nil, query, 
enums.GNS_LO_DEFAULT)
                                if err != nil {
                                        logger.Printf(logger.ERROR, "[dns] 
Can't resolve NS server '%s'\n", srv)
                                        continue
@@ -158,11 +165,17 @@ func (gns *GNSModule) ResolveDNS(name string, servers 
[]string, kind int, pkey *
                                switch int(rec.Type) {
                                case enums.GNS_TYPE_DNS_AAAA:
                                        addr = net.IP(rec.Data)
+                                       // we prefer IPv6
                                        break rec_loop
                                case enums.GNS_TYPE_DNS_A:
                                        addr = net.IP(rec.Data)
                                }
                        }
+                       // check if we have an IP address available
+                       if addr == nil {
+                               logger.Printf(logger.WARN, "[dns] No IP address 
for nameserver in GNS")
+                               continue
+                       }
                }
                // query DNS concurrently
                go queryDNS(idx, name, addr, kind, res)
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
index e4dc5fb..3e7bd18 100644
--- a/src/gnunet/service/gns/module.go
+++ b/src/gnunet/service/gns/module.go
@@ -1,7 +1,6 @@
 package gns
 
 import (
-       "encoding/hex"
        "fmt"
        "strings"
 
@@ -54,83 +53,40 @@ func NewQuery(pkey *ed25519.PublicKey, label string) *Query 
{
        }
 }
 
-//----------------------------------------------------------------------
-// GNS blocks with special types (PKEY, GNS2DNS) require special
-// treatment with respect to other resource records with different types
-// in the same block. Usually only certain other types (or not at all)
-// are allowed and the allowed ones are required to deliver a consistent
-// list of resulting resource records passed back to the caller.
-//----------------------------------------------------------------------
-
-// BlockHandler interface.
-type BlockHandler interface {
-       // TypeAction returns a flag indicating how a resource record of a
-       // given type is to be treated:
-       //   = -1: Record is not allowed (terminates lookup with an error)
-       //   =  0: Record is allowed but will be ignored
-       //   =  1: Record is allowed and will be processed
-       TypeAction(int) int
-}
-
-// Gns2DnsHandler implementing the BlockHandler interface
-type Gns2DnsHandler struct {
-       Name    string
-       Servers []string
-}
-
-// NewGns2DnsHandler returns a new BlockHandler instance
-func NewGns2DnsHandler() *Gns2DnsHandler {
-       return &Gns2DnsHandler{
-               Name:    "",
-               Servers: make([]string, 0),
-       }
-}
-
-// TypeAction return a flag indicating how a resource record of a given type
-// is to be treated (see RecordMaster interface)
-func (m *Gns2DnsHandler) TypeAction(t int) int {
-       // only process other GNS2DNS records
-       if t == enums.GNS_TYPE_GNS2DNS {
-               return 1
-       }
-       // skip everything else
-       return 0
-}
-
-// AddRequest adds the DNS request for "name" at "server" to the list
-// of requests. All GNS2DNS records must query for the same name
-func (m *Gns2DnsHandler) AddRequest(name, server string) bool {
-       if len(m.Servers) == 0 {
-               m.Name = name
-       }
-       if name != m.Name {
-               return false
-       }
-       m.Servers = append(m.Servers, server)
-       return true
-}
-
 //----------------------------------------------------------------------
 // The GNS module (recursively) resolves GNS names:
-// Resolves DNS-like names (e.g. "minecraft.servers.bob.games") to the
-// requested resource records (RRs). In short, the resolution process
-// works as follows:
+// Resolves DNS-like names (e.g. "minecraft.servers.bob.games"; a name is
+// a list of labels with '.' as separator) to the requested resource
+// records (RRs). In short, the resolution process works as follows:
 //
 //  Resolve(name):
 //  --------------
-//  (1) split the full name into elements in reverse order: names[]
-//  (2) Resolve first element (root zone, right-most name part, name[0]) to
+//  (1) split the name ('.' as separator) into labels in reverse order: 
labels[]
+//  (2) Resolve first label (= root zone, right-most name part, labels[0]) to
 //      a zone public key PKEY:
-//      (a) the name is a string representation of a public key -> (3)
-//      (b) the zone key for the name is stored in the config file -> (3)
-//      (c) a local zone with that given name -> (3)
+//      (a) the label is a string representation of a public key -> (3)
+//      (b) the zone key for the label is stored in the config file -> (3)
+//      (c) a local zone with that given label -> (3)
 //      (d) ERROR: "Unknown root zone"
-//  (3) names = names[1:] // remove first element
-//      block = Lookup (PKEY, names[0]):
-//      (a) If last element of namess: -> (4)
-//      (b) block is PKEY record:
-//          PKEY <- block, --> (3)
-//  (4) return block: it is the responsibility of the caller to assemble
+//  (3) labels = labels[1:]
+//      records = Resolve (labels[0], PKEY)
+//      If last label in name: -> (5)
+//  (4) for all rec in records:
+//          (a) if rec is a PKEY record:
+//                  PKEY <- record, --> (3)
+//          (b) if rec is a GNS2DNS record:
+//                  delegate to DNS to resolve rest of name -> (5)
+//          (c) if rec is BOX record:
+//                  if rest of name is pattern "_service._proto" and matches
+//                  the values in the BOX:
+//                      Replace records with resource record from BOX -> (5)
+//          (d) if rec is CNAME record:
+//                  if no remaining labels:
+//                      if requested types include CNAME -> (5)
+//                      if
+//      resolution failed: name not completely processed and no zone available
+//
+//  (5) return records: it is the responsibility of the caller to assemble
 //      the desired result from block data (e.g. filter for requested
 //      resource record types).
 //----------------------------------------------------------------------
@@ -145,10 +101,10 @@ type GNSModule struct {
        GetLocalZone func(name string) (*ed25519.PublicKey, error)
 }
 
-// Resolve a GNS name with multiple elements, If pkey is not nil, the name
+// Resolve a GNS name with multiple labels. If pkey is not nil, the name
 // is interpreted as "relative to current zone".
-func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind int, 
mode int) (set *GNSRecordSet, err error) {
-       // get the name elements in reverse order
+func (gns *GNSModule) Resolve(path string, pkey *ed25519.PublicKey, kind 
RRTypeList, mode int) (set *GNSRecordSet, err error) {
+       // get the labels in reverse order
        names := util.ReverseStringList(strings.Split(path, "."))
        logger.Printf(logger.DBG, "[gns] Resolver called for %v\n", names)
 
@@ -161,8 +117,8 @@ func (gns *GNSModule) Resolve(path string, pkey 
*ed25519.PublicKey, kind int, mo
        return gns.ResolveAbsolute(names, kind, mode)
 }
 
-// Resolve a fully qualified GNS absolute name (with multiple levels).
-func (gns *GNSModule) ResolveAbsolute(names []string, kind int, mode int) (set 
*GNSRecordSet, err error) {
+// Resolve a fully qualified GNS absolute name (with multiple labels).
+func (gns *GNSModule) ResolveAbsolute(labels []string, kind RRTypeList, mode 
int) (set *GNSRecordSet, err error) {
        // get the root zone key for the TLD
        var (
                pkey *ed25519.PublicKey
@@ -170,40 +126,43 @@ func (gns *GNSModule) ResolveAbsolute(names []string, 
kind int, mode int) (set *
        )
        for {
                // (1) check if TLD is a public key string
-               if len(names[0]) == 52 {
-                       if data, err = util.DecodeStringToBinary(names[0], 32); 
err == nil {
+               if len(labels[0]) == 52 {
+                       if data, err = util.DecodeStringToBinary(labels[0], 
32); err == nil {
                                if pkey = ed25519.NewPublicKeyFromBytes(data); 
pkey != nil {
                                        break
                                }
                        }
                }
                // (2) check if TLD is in our local config
-               if pkey = config.Cfg.GNS.GetRootZoneKey(names[0]); pkey != nil {
+               if pkey = config.Cfg.GNS.GetRootZoneKey(labels[0]); pkey != nil 
{
                        break
                }
                // (3) check if TLD is one of our identities
-               if pkey, err = gns.GetLocalZone(names[0]); err == nil {
+               if pkey, err = gns.GetLocalZone(labels[0]); err == nil {
                        break
                }
                // (4) we can't resolve this TLD
                return nil, ErrUnknownTLD
        }
        // continue with resolution relative to a zone.
-       return gns.ResolveRelative(names[1:], pkey, kind, mode)
+       return gns.ResolveRelative(labels[1:], pkey, kind, mode)
 }
 
 // Resolve relative path (to a given zone) recursively by processing simple
 // (PKEY,Label) lookups in sequence and handle intermediate GNS record types
-func (gns *GNSModule) ResolveRelative(names []string, pkey *ed25519.PublicKey, 
kind int, mode int) (set *GNSRecordSet, err error) {
+func (gns *GNSModule) ResolveRelative(labels []string, pkey 
*ed25519.PublicKey, kind RRTypeList, mode int) (set *GNSRecordSet, err error) {
        // Process all names in sequence
-       var records []*message.GNSResourceRecord
+       var (
+               records []*message.GNSResourceRecord // final resource records 
from resolution
+               hdlrs   *BlockHandlerList            // list of block handlers 
in final step
+       )
 name_loop:
-       for ; len(names) > 0; names = names[1:] {
-               logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in 
'%s'\n", names[0], util.EncodeBinaryToString(pkey.Bytes()))
+       for ; len(labels) > 0; labels = labels[1:] {
+               logger.Printf(logger.DBG, "[gns] ResolveRelative '%s' in 
'%s'\n", labels[0], util.EncodeBinaryToString(pkey.Bytes()))
 
                // resolve next level
                var block *GNSBlock
-               if block, err = gns.Lookup(pkey, names[0], mode == 
enums.GNS_LO_DEFAULT); err != nil {
+               if block, err = gns.Lookup(pkey, labels[0], mode == 
enums.GNS_LO_DEFAULT); err != nil {
                        // failed to resolve name
                        return
                }
@@ -213,94 +172,64 @@ name_loop:
                }
                // post-process block by inspecting contained resource records 
for
                // special GNS types
-               var hdlr BlockHandler
                if records, err = block.Records(); err != nil {
                        return
                }
-               for _, rec := range records {
-                       // let a block handler decide how to handle records
-                       if hdlr != nil {
-                               switch hdlr.TypeAction(int(rec.Type)) {
-                               case -1:
-                                       // No records of this type allowed in 
block
-                                       err = ErrInvalidRecordType
-                                       return
-                               case 0:
-                                       // records of this type are simply 
ignored
-                                       continue
-                               case 1:
-                                       // process record of this type
-                               }
-                       }
-                       switch int(rec.Type) {
-                       
//----------------------------------------------------------
-                       case enums.GNS_TYPE_PKEY:
-                               // check for single RR and sane key data
-                               if len(rec.Data) != 32 || len(records) > 1 {
-                                       err = ErrInvalidPKEY
-                                       return
-                               }
-                               // set new PKEY and continue resolution
-                               pkey = ed25519.NewPublicKeyFromBytes(rec.Data)
-                               continue name_loop
+               // assemble a list of block handlers for this block: if multiple
+               // block handlers are present, they are consistent with all 
block
+               // records.
+               if hdlrs, err = NewBlockHandlerList(records, labels[1:]); err 
!= nil {
+                       // conflicting block handler records found: terminate 
with error.
+                       // (N.B.: The BlockHandlerList class executes the logic 
which mix
+                       // of resource records in a single block is considered 
valid.)
+                       return
+               }
 
-                       
//----------------------------------------------------------
-                       case enums.GNS_TYPE_GNS2DNS:
-                               // get the master controlling this block; 
create a new
-                               // one if necessary
-                               var inst *Gns2DnsHandler
-                               if hdlr == nil {
-                                       inst = NewGns2DnsHandler()
-                                       hdlr = inst
-                               } else {
-                                       inst = hdlr.(*Gns2DnsHandler)
-                               }
-                               // extract list of names in DATA block:
-                               logger.Printf(logger.DBG, "[gns] GNS2DNS data: 
%s\n", hex.EncodeToString(rec.Data))
-                               var dnsNames []string
-                               for pos := 0; ; {
-                                       next, name := 
DNSNameFromBytes(rec.Data, pos)
-                                       if len(name) == 0 {
-                                               break
-                                       }
-                                       dnsNames = append(dnsNames, name)
-                                       pos = next
-                               }
-                               logger.Printf(logger.DBG, "[gns] GNS2DNS 
params: %v\n", dnsNames)
-                               if len(dnsNames) != 2 {
-                                       err = ErrInvalidRecordBody
-                                       return
-                               }
-                               // Add to collection of requests
-                               logger.Printf(logger.DBG, "[gns] GNS2DNS: query 
for '%s' on '%s'\n", dnsNames[0], dnsNames[1])
-                               if !inst.AddRequest(dnsNames[0], dnsNames[1]) {
-                                       err = ErrInvalidRecordBody
-                                       return
-                               }
+               //--------------------------------------------------------------
+               // handle special block cases in priority order:
+               //--------------------------------------------------------------
+
+               if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_PKEY); hdlr != nil {
+                       // (1) PKEY record:
+                       inst := hdlr.(*PkeyHandler)
+                       // if labels are pending, set new zone and continue 
resolution
+                       if len(labels) > 1 {
+                               pkey = inst.pkey
+                               continue name_loop
                        }
-               }
-               // handle special block cases
-               if hdlr != nil {
-                       switch inst := hdlr.(type) {
-                       case *Gns2DnsHandler:
-                               // we need to handle delegation to DNS: returns 
a list of found
-                               // resource records in DNS (filter by 'kind')
-                               fqdn := 
strings.Join(util.ReverseStringList(names[1:]), ".") + "." + inst.Name
-                               if set, err = gns.ResolveDNS(fqdn, 
inst.Servers, kind, pkey); err != nil {
-                                       logger.Println(logger.ERROR, "[gns] 
GNS2DNS resilution failed.")
-                                       return
-                               }
-                               // we are done with resolution; pass on records 
to caller
-                               records = set.Records
+               } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS); 
hdlr != nil {
+                       // (2) GNS2DNS records: delegate resolution to DNS
+                       inst := hdlr.(*Gns2DnsHandler)
+                       // we need to handle delegation to DNS: returns a list 
of found
+                       // resource records in DNS (filter by 'kind')
+                       lbls := 
strings.Join(util.ReverseStringList(labels[1:]), ".")
+                       if len(lbls) > 0 {
+                               lbls += "."
+                       }
+                       fqdn := lbls + inst.Name
+                       if set, err = gns.ResolveDNS(fqdn, inst.Servers, kind, 
pkey); err != nil {
+                               logger.Println(logger.ERROR, "[gns] GNS2DNS 
resolution failed.")
+                               return
+                       }
+                       // we are done with resolution; pass on records to 
caller
+                       records = set.Records
+                       break name_loop
+               } else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_BOX); hdlr != 
nil {
+                       // (3) BOX records:
+                       inst := hdlr.(*BoxHandler)
+                       new_records := inst.Records(kind).Records
+                       if len(new_records) > 0 {
+                               records = new_records
                                break name_loop
                        }
                }
        }
-       // Assemble resulting resource record set
+       // Assemble resulting resource record set by filtering for requested 
types.
+       // Records might get transformed by active block handlers.
        set = NewGNSRecordSet()
        for _, rec := range records {
                // is this the record type we are looking for?
-               if kind == enums.GNS_TYPE_ANY || int(rec.Type) == kind {
+               if kind.HasType(int(rec.Type)) {
                        // add it to the result
                        set.AddRecord(rec)
                }
@@ -321,7 +250,6 @@ func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, label 
string, remote bool)
                return
        }
        if block == nil {
-               logger.Println(logger.DBG, "[gns] local Lookup: no block found")
                if remote {
                        // get the block from a remote lookup
                        if block, err = gns.LookupRemote(query); err != nil || 
block == nil {
@@ -330,12 +258,15 @@ func (gns *GNSModule) Lookup(pkey *ed25519.PublicKey, 
label string, remote bool)
                                        block = nil
                                } else {
                                        logger.Println(logger.DBG, "[gns] 
remote Lookup: no block found")
+                                       err = fmt.Errorf("No block found")
                                }
                                // lookup fails completely -- no result
                                return
                        }
                        // store RRs from remote locally.
                        gns.StoreLocal(query, block)
+               } else {
+                       err = fmt.Errorf("No block found")
                }
        }
        return
diff --git a/src/gnunet/service/gns/service.go 
b/src/gnunet/service/gns/service.go
index 852f513..dd516ff 100644
--- a/src/gnunet/service/gns/service.go
+++ b/src/gnunet/service/gns/service.go
@@ -4,9 +4,6 @@ import (
        "encoding/hex"
        "io"
 
-       "github.com/bfix/gospel/crypto/ed25519"
-       "github.com/bfix/gospel/data"
-       "github.com/bfix/gospel/logger"
        "gnunet/config"
        "gnunet/crypto"
        "gnunet/enums"
@@ -14,6 +11,10 @@ import (
        "gnunet/service"
        "gnunet/transport"
        "gnunet/util"
+
+       "github.com/bfix/gospel/crypto/ed25519"
+       "github.com/bfix/gospel/data"
+       "github.com/bfix/gospel/logger"
 )
 
 //----------------------------------------------------------------------
@@ -77,7 +78,8 @@ func (s *GNSService) ServeClient(mc *transport.MsgChannel) {
                        //       access to the message channel to send 
responses)
                        pkey := ed25519.NewPublicKeyFromBytes(m.Zone)
                        label := m.GetName()
-                       recset, err := s.Resolve(label, pkey, int(m.Type), 
int(m.Options))
+                       kind := NewRRTypeList(int(m.Type))
+                       recset, err := s.Resolve(label, pkey, kind, 
int(m.Options))
                        if err != nil {
                                logger.Printf(logger.ERROR, "[gns] Failed to 
lookup block: %s\n", err.Error())
                                break
@@ -146,7 +148,7 @@ func (s *GNSService) LookupNamecache(query *Query) (block 
*GNSBlock, err error)
                        break
                }
                // check if block was found
-               if len(m.EncData) == 0 {
+               if len(m.EncData) == 0 || util.IsNull(m.EncData) {
                        logger.Println(logger.DBG, "[gns] block not found in 
namecache")
                        break
                }
diff --git a/src/gnunet/service/service.go b/src/gnunet/service/service.go
index 5b44d47..6aab226 100644
--- a/src/gnunet/service/service.go
+++ b/src/gnunet/service/service.go
@@ -3,8 +3,9 @@ package service
 import (
        "fmt"
 
-       "github.com/bfix/gospel/logger"
        "gnunet/transport"
+
+       "github.com/bfix/gospel/logger"
 )
 
 // Service is an interface for GNUnet services. Every service has one channel
@@ -48,7 +49,7 @@ func (si *ServiceImpl) Start(spec string) (err error) {
        }
 
        // start channel server
-       logger.Printf(logger.DBG, "[%s] Service starting.\n", si.name)
+       logger.Printf(logger.INFO, "[%s] Service starting.\n", si.name)
        if si.srvc, err = transport.NewChannelServer(spec, si.hdlr); err != nil 
{
                return
        }
@@ -61,19 +62,19 @@ func (si *ServiceImpl) Start(spec string) (err error) {
                        select {
                        case in := <-si.hdlr:
                                if in == nil {
-                                       logger.Printf(logger.DBG, "[%s] 
Listener terminated.\n", si.name)
+                                       logger.Printf(logger.INFO, "[%s] 
Listener terminated.\n", si.name)
                                        break loop
                                }
                                switch ch := in.(type) {
                                case transport.Channel:
-                                       logger.Printf(logger.DBG, "[%s] Client 
connected.\n", si.name)
+                                       logger.Printf(logger.INFO, "[%s] Client 
connected.\n", si.name)
                                        go 
si.impl.ServeClient(transport.NewMsgChannel(ch))
                                }
                        case <-si.ctrl:
                                break loop
                        }
                }
-               logger.Printf(logger.DBG, "[%s] Service closing.\n", si.name)
+               logger.Printf(logger.INFO, "[%s] Service closing.\n", si.name)
                si.srvc.Close()
                si.running = false
        }()
@@ -89,7 +90,7 @@ func (si *ServiceImpl) Stop() error {
        }
        si.running = false
        si.ctrl <- true
-       logger.Printf(logger.DBG, "[%s] Service terminating.\n", si.name)
+       logger.Printf(logger.INFO, "[%s] Service terminating.\n", si.name)
 
        return si.impl.Stop()
 }
diff --git a/src/gnunet/transport/channel.go b/src/gnunet/transport/channel.go
index 8502d8f..4005759 100644
--- a/src/gnunet/transport/channel.go
+++ b/src/gnunet/transport/channel.go
@@ -11,6 +11,7 @@ import (
        "gnunet/message"
 )
 
+// Error codes
 var (
        ErrChannelNotImplemented = fmt.Errorf("Protocol not implemented")
        ErrChannelNotOpened      = fmt.Errorf("Channel not opened")
diff --git a/src/gnunet/transport/channel_netw.go 
b/src/gnunet/transport/channel_netw.go
index 69ddda5..ecfd8e2 100644
--- a/src/gnunet/transport/channel_netw.go
+++ b/src/gnunet/transport/channel_netw.go
@@ -14,11 +14,12 @@ import (
 
 // NetworkChannel
 type NetworkChannel struct {
-       network string
-       conn    net.Conn
+       network string   // network protocol identifier ("tcp", "unix", ...)
+       conn    net.Conn // associated connection
 }
 
-// NewNetworkChannel
+// NewNetworkChannel creates a new channel for a given network protocol.
+// The channel is in pending state and need to be opened before use.
 func NewNetworkChannel(netw string) Channel {
        return &NetworkChannel{
                network: netw,
@@ -26,7 +27,11 @@ func NewNetworkChannel(netw string) Channel {
        }
 }
 
-// Open
+// Open a network channel based on specification:
+// The specification is a string separated into parts by the '+' delimiter
+// (e.g. "unix+/tmp/gnunet-service-gns-go.sock+perm=0770"). The network
+// identifier (first part) must match the network specification of the
+// underlaying NetworkChannel instance.
 func (c *NetworkChannel) Open(spec string) (err error) {
        parts := strings.Split(spec, "+")
        // check for correct protocol
@@ -38,7 +43,7 @@ func (c *NetworkChannel) Open(spec string) (err error) {
        return
 }
 
-// Close
+// Close a network channel
 func (c *NetworkChannel) Close() error {
        if c.conn != nil {
                return c.conn.Close()
@@ -46,7 +51,8 @@ func (c *NetworkChannel) Close() error {
        return ErrChannelNotOpened
 }
 
-// Read
+// Read bytes from a network channel into buffer: Returns the number of read
+// bytes and an error code. Only works on open channels ;)
 func (c *NetworkChannel) Read(buf []byte) (int, error) {
        if c.conn == nil {
                return 0, ErrChannelNotOpened
@@ -54,7 +60,8 @@ func (c *NetworkChannel) Read(buf []byte) (int, error) {
        return c.conn.Read(buf)
 }
 
-// Write
+// Write buffer to a network channel: Returns the number of written bytes and
+// an error code.
 func (c *NetworkChannel) Write(buf []byte) (int, error) {
        if c.conn == nil {
                return 0, ErrChannelNotOpened
@@ -67,8 +74,8 @@ func (c *NetworkChannel) Write(buf []byte) (int, error) {
 
 // NetworkChannelServer
 type NetworkChannelServer struct {
-       network  string
-       listener net.Listener
+       network  string       // network protocol to listen on
+       listener net.Listener // reference to listener object
 }
 
 // NewNetworkChannelServer
@@ -79,7 +86,9 @@ func NewNetworkChannelServer(netw string) ChannelServer {
        }
 }
 
-// Open
+// Open a network channel server (= start running it) based on the given
+// specification. For every client connection to the server, the associated
+// network channel for the connection is send via the hdlr channel.
 func (s *NetworkChannelServer) Open(spec string, hdlr chan<- Channel) (err 
error) {
        parts := strings.Split(spec, "+")
        // check for correct protocol
@@ -136,7 +145,7 @@ func (s *NetworkChannelServer) Open(spec string, hdlr 
chan<- Channel) (err error
        return nil
 }
 
-// Close
+// Close a network channel server (= stop the server)
 func (s *NetworkChannelServer) Close() error {
        if s.listener != nil {
                err := s.listener.Close()
@@ -147,27 +156,35 @@ func (s *NetworkChannelServer) Close() error {
 }
 
 ////////////////////////////////////////////////////////////////////////
+// helper functions to instantiate network channels and servers for
+// common network protocols
 
+// NewSocketChannel: Unix Domain Socket connection
 func NewSocketChannel() Channel {
        return NewNetworkChannel("unix")
 }
 
+// NewTCPChannel: TCP connection
 func NewTCPChannel() Channel {
        return NewNetworkChannel("tcp")
 }
 
+// NewUDPChannel: UDP connection
 func NewUDPChannel() Channel {
        return NewNetworkChannel("udp")
 }
 
+// NewSocketChannelServer: Unix Domain Socket listener
 func NewSocketChannelServer() ChannelServer {
        return NewNetworkChannelServer("unix")
 }
 
+// NewTCPChannelServer: TCP listener
 func NewTCPChannelServer() ChannelServer {
        return NewNetworkChannelServer("tcp")
 }
 
+// NewUDPChannelServer: UDP listener
 func NewUDPChannelServer() ChannelServer {
        return NewNetworkChannelServer("udp")
 }
diff --git a/src/gnunet/transport/connection.go 
b/src/gnunet/transport/connection.go
index e66bec1..1cf0317 100644
--- a/src/gnunet/transport/connection.go
+++ b/src/gnunet/transport/connection.go
@@ -5,7 +5,6 @@ import (
        "gnunet/message"
 )
 
-////////////////////////////////////////////////////////////////////////
 // Connection for communicating peers
 type Connection struct {
        from, to  *core.Peer
@@ -17,6 +16,8 @@ type Connection struct {
        shared    []byte
 }
 
+// NewConnection instanciates a new connection between peers communicating
+// over a message channel (Connections are authenticated and secured).
 func NewConnection(ch *MsgChannel, from, to *core.Peer) *Connection {
        return &Connection{
                from:  from,
@@ -26,27 +27,33 @@ func NewConnection(ch *MsgChannel, from, to *core.Peer) 
*Connection {
        }
 }
 
+// SharedSecret computes the shared secret the two endpoints of a connection.
 func (c *Connection) SharedSecret(secret []byte) {
        c.shared = make([]byte, len(secret))
        copy(c.shared, secret)
 }
 
+// GetState returns the current state of the connection.
 func (c *Connection) GetState() int {
        return c.state
 }
 
+// SetBandwidth to control transfer rates on the connection
 func (c *Connection) SetBandwidth(bw uint32) {
        c.bandwidth = bw
 }
 
+// Close connection between two peers.
 func (c *Connection) Close() error {
        return c.ch.Close()
 }
 
+// Send a message on the connection
 func (c *Connection) Send(msg message.Message) error {
        return c.ch.Send(msg)
 }
 
+// Receive a message on the connection
 func (c *Connection) Receive() (message.Message, error) {
        return c.ch.Receive()
 }
diff --git a/src/gnunet/transport/session.go b/src/gnunet/transport/session.go
index 90e4016..7d33ea2 100644
--- a/src/gnunet/transport/session.go
+++ b/src/gnunet/transport/session.go
@@ -1,7 +1,6 @@
 package transport
 
-import ()
-
+// Session states
 const (
        KX_STATE_DOWN         = iota // No handshake yet.
        KX_STATE_KEY_SENT            // We've sent our session key.
diff --git a/src/gnunet/util/address.go b/src/gnunet/util/address.go
index 04e2254..37fb102 100644
--- a/src/gnunet/util/address.go
+++ b/src/gnunet/util/address.go
@@ -1,29 +1,19 @@
 package util
 
 import (
+       "encoding/hex"
        "fmt"
+       "net"
 )
 
-type IPAddress struct {
-       Host []byte `size:"*-2"`
-       Port uint16 `order:"big"`
-}
-
-func NewIPAddress(host []byte, port uint16) *IPAddress {
-       ip := &IPAddress{
-               Host: make([]byte, len(host)),
-               Port: port,
-       }
-       copy(ip.Host, host)
-       return ip
-}
-
+// Address specifies how a peer is reachable on the network.
 type Address struct {
-       Transport string
-       Options   uint32 `order:"big"`
-       Address   []byte `size:"*"`
+       Transport string // transport protocol
+       Options   uint32 `order:"big"` // address options
+       Address   []byte `size:"*"`    // address data (protocol-dependent)
 }
 
+// NewAddress returns a new Address for the given transport and specs
 func NewAddress(transport string, addr []byte) *Address {
        a := &Address{
                Transport: transport,
@@ -34,6 +24,37 @@ func NewAddress(transport string, addr []byte) *Address {
        return a
 }
 
+// String returns a human-readable representation of an address.
 func (a *Address) String() string {
        return fmt.Sprintf("Address{%s}", AddressString(a.Transport, a.Address))
 }
+
+//----------------------------------------------------------------------
+
+// AddressString returns a string representaion of an address.
+func AddressString(transport string, addr []byte) string {
+       if transport == "tcp" || transport == "udp" {
+               alen := len(addr)
+               port := uint(addr[alen-2])*256 + uint(addr[alen-1])
+               return fmt.Sprintf("%s:%s:%d", transport, 
net.IP(addr[:alen-2]).String(), port)
+       }
+       return fmt.Sprintf("%s:%s", transport, hex.EncodeToString(addr))
+}
+
+//----------------------------------------------------------------------
+
+// IP address (can be IPv4 or IPv6 or a DNS name)
+type IPAddress struct {
+       Host []byte `size:"*-2"`
+       Port uint16 `order:"big"`
+}
+
+// NewIPAddress creates a new instance for a given host and port.
+func NewIPAddress(host []byte, port uint16) *IPAddress {
+       ip := &IPAddress{
+               Host: make([]byte, len(host)),
+               Port: port,
+       }
+       copy(ip.Host, host)
+       return ip
+}
diff --git a/src/gnunet/util/array.go b/src/gnunet/util/array.go
index 9076516..f6213bf 100644
--- a/src/gnunet/util/array.go
+++ b/src/gnunet/util/array.go
@@ -30,6 +30,16 @@ func Reverse(b []byte) []byte {
        return r
 }
 
+// IsNull returns true if all bytes in an array are set to 0.
+func IsNull(b []byte) bool {
+       for _, v := range b {
+               if v != 0 {
+                       return false
+               }
+       }
+       return true
+}
+
 // CopyBlock copies 'in' to 'out' so that 'out' is filled completely.
 // - If 'in' is larger than 'out', it is left-truncated before copy
 // - If 'in' is smaller than 'out', it is left-padded with 0 before copy
diff --git a/src/gnunet/util/format.go b/src/gnunet/util/format.go
index 722b9a7..780c814 100644
--- a/src/gnunet/util/format.go
+++ b/src/gnunet/util/format.go
@@ -1,22 +1,13 @@
 package util
 
 import (
-       "encoding/hex"
        "fmt"
-       "net"
 )
 
-func AddressString(transport string, addr []byte) string {
-       if transport == "tcp" || transport == "udp" {
-               alen := len(addr)
-               port := uint(addr[alen-2])*256 + uint(addr[alen-1])
-               return fmt.Sprintf("%s:%s:%d", transport, 
net.IP(addr[:alen-2]).String(), port)
-       }
-       return fmt.Sprintf("%s:%s", transport, hex.EncodeToString(addr))
-}
-
 var scale = " kMGTPEO"
 
+// Scale1024 returns an integer value (e.g. a size) as a human-readable
+// string with scales: a size of 183467245 would result in "174,967M"
 func Scale1024(n uint64) string {
        v := float64(n)
        var i int
diff --git a/src/gnunet/util/id.go b/src/gnunet/util/id.go
index 41bd30e..ab9b98c 100644
--- a/src/gnunet/util/id.go
+++ b/src/gnunet/util/id.go
@@ -4,7 +4,8 @@ var (
        _id = 0
 )
 
+// generate next unique identifier (unique in the running process/application)
 func NextID() int {
-       _id += 1
+       _id++
        return _id
 }
diff --git a/src/gnunet/util/peer_id.go b/src/gnunet/util/peer_id.go
index 03bc73e..6549d75 100644
--- a/src/gnunet/util/peer_id.go
+++ b/src/gnunet/util/peer_id.go
@@ -1,9 +1,11 @@
 package util
 
+// PeerID is the 32-byte binary representation od a Ed25519 key
 type PeerID struct {
        Key []byte `size:"32"`
 }
 
+// NewPeerID creates a new object from the data.
 func NewPeerID(data []byte) *PeerID {
        if data == nil {
                data = make([]byte, 32)
@@ -22,6 +24,7 @@ func NewPeerID(data []byte) *PeerID {
        }
 }
 
+// String returns a human-readable representation of a peer id.
 func (p *PeerID) String() string {
        return EncodeBinaryToString(p.Key)
 }
diff --git a/src/gnunet/util/rnd.go b/src/gnunet/util/rnd.go
index d3c8b2e..a9f247f 100644
--- a/src/gnunet/util/rnd.go
+++ b/src/gnunet/util/rnd.go
@@ -6,16 +6,19 @@ import (
        "encoding/binary"
 )
 
+// RndArray fills a buffer with random content
 func RndArray(b []byte) {
        rand.Read(b)
 }
 
+// NewRndArray creates a new buffer of given size; filled with random content.
 func NewRndArray(size int) []byte {
        b := make([]byte, size)
        rand.Read(b)
        return b
 }
 
+// RndUInt64 returns a new 64-bit unsigned random integer.
 func RndUInt64() uint64 {
        b := make([]byte, 8)
        RndArray(b)
@@ -25,22 +28,27 @@ func RndUInt64() uint64 {
        return v
 }
 
+// RndInt64 returns a new 64-bit signed random integer.
 func RndInt64() int64 {
        return int64(RndUInt64())
 }
 
+// RndUInt32 returns a new 32-bit unsigned random integer.
 func RndUInt32() uint32 {
        return uint32(RndUInt64())
 }
 
+// RndInt32 returns a new 32-bit signed random integer.
 func RndInt32() int32 {
        return int32(RndUInt64())
 }
 
+// RndUInt16 returns a new 16-bit unsigned random integer.
 func RndUInt16() uint16 {
        return uint16(RndUInt64())
 }
 
+// RndInt16 returns a new 16-bit signed random integer.
 func RndInt16() int16 {
        return int16(RndUInt64())
 }

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]