Pi cluster fun

I've bought 3 Raspberry Pis (B+, a week before the announcement of the Pi 2), and created a little cluster with them. Each Pi is connected to wireless ( and they share an internal, wired, network.

Virtual IP

I want one IP address to be load balanced across the three Pis. This can be with the CLUSTERIP target in iptables. In raspbian the kernel module for this was not enabled, but luckily this was fixed very quickly.

The virtual IP address is and creating it is done like so:

auto wlan0:0
iface wlan0:0 inet static
pre-up iptables -A INPUT -d -j CLUSTERIP -i wlan0 \
--new --hashmode sourceip-sourceport --clustermac 01:00:5E:00:00:01 \
--total-nodes 3 --local-node 2
pre-down iptables -D INPUT -d -j CLUSTERIP -i wlan0 \
--new --hashmode sourceip-sourceport --clustermac 01:00:5E:00:00:01 \
--total-nodes 3 --local-node 2

The mac address (01:00:5E:00:00:01) is a multicast mac address. The above snippet has to be run on each of the Pis; this is on my second node (--local-node 2). This also needs to some health checking to remove nodes from the config when they are dead. This is still on the todo list.

If all this went correct you can ping this IP. You would see the icmp packets arriving at all hosts, but only one will reply (yes, this is a poor man's load balancing).


Go has excellent cross compiling support and ARM support. This means we are able to cross compile Etcd and run it.

First compile Go to be able to make ARM binaries:

cd $GOROOT/src
GOARCH=arm ./make.bash

Then compile Etcd for ARM:

cd github.com/cores/etcd
GOARCH=arm GOARM=5 go build

When done, you have a binary for ARM:

% file etcd
etcd: ELF 32-bit LSB  executable, ARM, EABI5 version 1 (SYSV), statically linked, not stripped

When running this on my Pis, it crashed on startup, which can be fixed with a small patch that is included in that Etcd issue (full fix is pending).

Then I use zbundle, to combine the startup code with the etcd binary and copy the resulting (executable) zipfile to the Pis.

Startup code (contained in a file temp. called x.sh)

unzip -qo $0 2>/dev/null  # needed for zbundle

TENIP=$(ip -4 -o addr sh eth0 | awk ' { print $4 } ' | sed 's|/[0-9][0-9]||')


CMD="./etcd -name $HOST -initial-advertise-peer-urls http://$TENIP:2380
  -listen-peer-urls "$PEER,"http://$TENIP:2380
  -listen-client-urls $CLIENT
  -advertise-client-urls $ADVERT
  -initial-cluster-token pi-etcd
  -initial-cluster pi0=,pi1=,pi2=
  -initial-cluster-state new"

echo $CMD
$CMD &

All of the above tells etcd that it should contact the other members via the address and listen for client connections on the virtual IP of the wireless network.

Bundle and deploy (who needs Docker anyway?):

% zbundle x.sh etcd
  adding: etcd (deflated 69%)

% for i in 0 1 2; do scp etcd.zip pi$i: ; done
etcd.zip                            100% 2368KB   2.3MB/s   00:00
etcd.zip                            100% 2368KB   2.3MB/s   00:00
etcd.zip                            100% 2368KB   2.3MB/s   00:01

Now on each the Pis, we can just execute ./etcd.zip and "stuff" should happen.

And then from my laptop:

% etcdctl -C set /foo/bar "Hello world" --ttl 60
Hello world

% ./etcdctl -C ls /foo/bar

% ./etcdctl -C get /foo/bar
Hello world

So \o/ yes, this works.

Now on with my next plans: Putting SkyDNS on the same virtual IP, creating a DHCP server that used Etcd as a storage backend and getting this all to work with IPv6.

Tagged , , , , ,

Markdown for XML2RFC v3 - Update

And another update (2015-01-12)

This is a follow up the previous article about using markdown for XML2RFC. The syntax has been changed and more features have been implement. And my blackfriday fork has been renamed mmark.

See this document for an up to date syntax of mmark. A short summery will be given here.

New features are:

Also the possibility to convert pandoc2rfc documents to mmark has been added (as a quick and dirty perl script + pandoc!):

For example converting the middle section from some other draft:

% pandoc --atx-headers -t markdown_phpextra < ~/i-d/nsec4/middle.mkd |\
    ./parts.pl | tee part.md

For a first pass for getting a (non) perfect title block you could use convert/titleblock.pl.


Syntax has been changed slightly.

Use the reference syntax: [@RFC2535 p. 23].

They can optionally have '!' or '?' modifier to normative or informative reference. The reference section is automatically generated. Here is a normative and informative reference: [@!RFC2335] and [@?RFC2335].

The default is informative. This only have to be done once, after that you can just use [@RFC2535]. And an even shorter syntax has been added, just @RFC2535, works once a citation has been defined.


Start the section with the special header syntax .#. The section's name must be abstract (case insensitive). (Colophon and friends might be added as well).

Notes and Asides

Asides is created with prefixing each line of a paragraph with A>, notes: N>.


Using triple parentheses, like so: (((Cats, Tiger))). You can now only add a primary index with !: (((!Cats, Tiger))).


Use -# as a header to start a new part.


The double $$ is detected and anything inside is math. If an entire paragraph consists out of math, display mode is used.

Tagged , , ,

Markdown for XML2RFC v3

Or... how to write Markdown to generate XML that is valid XML2RFC v3. These are some assorted notes and ideas


  1. Self contained file with all information to generate a complete I-D;
  2. CommonMarkdown, with some extension.

I'm working on implementing these ideas in blackfriday. The following has already been implemented:

  • Abstract;
  • Citation;
  • Document divisions;
  • Index;
  • File inclusion;
  • Extra attributes (kramdown's IAL.) (work-in-progress).

Document meta data

In TOML - much easier than YAML for instance, lines are starting with %. We don't need to specify the citations here because:


Use the reference syntax: [p. 23][#RFC2535], which can be extended to include a filename where the reference is:

[p. 23][#RFC2335,bib/reference.RFC.2525.xml]

They can optionally have 'n' or 'i' modifier to normative or informative reference. The reference section is automatically generated. Here is a normative and informative reference:


The default is informative. This only have to be done once, after that you can just use [][#RFC2535].

Abstract, notes and asides

Abstract is done with prefixing a paragraph with AB>, notes: N> and aside with A>.

Extra Attributes

CommonMark (seems?) to have adapted the kramdown IAL syntax but dropped the colon, so we would use {.class key=value} to add attributes to block elements.

Document Divisions

The front-, middle- and back-sections are signalled with:

{frontmatter}, {middlematter} and {backmatter}, where a document automatically starts with a frontmatter.


Using triple parentheses, like so: (((Cats, Tiger))).

Including Markdown files

Use the syntax {{file}} will get file included.

All of the above use the extension syntax, i.e. starting with a { .

Quotes attribution?

> this is an quote
-- Miek Gieben

BCP14 keywords?

Uppercase in asterisks *MUST*.

Tagged , , ,

xmodmap is dead

I'm using an Apple keyboard at work since a day or two, but for some reason this keyboard has a special key for eject, because in 1898 that was useful when you wanted to eject a cdrom.

Apple Keyboard

Anyway making this key do something useful proved incredible painful, especially since xmodmap is deprecated and we should all use xkb (setxkbmap andxkbcomp). In our new world of hotplugging everything this kinda makes sense (xmodmap settings are lost when your USB keyboard goes to sleep).

I use i3, but with gnome-settings-daemon running. So first I removed the eject functionality in there (not tested if this still is needed, with the later fix). In dconf-editor, go to org.gnome.settings-daemon.plugins.media-keys and remove the Value from the key named eject.

Searching more I found this site, which gave me to solution I settled on. With xev I found out the keycode sent is 169.

Dump the current config with xkbcomp $DISPLAY, this creates a file that will be called server-0.xkb

Find the line where keycode 169 is mapped:

key <I167> {         [     XF86Forward ] };
key <I169> {         [       XF86Eject ] };
key <I170> {         [       XF86Eject,       XF86Eject ] };

And change the key <I169> on to use BackSpace (for instance). Compile it to an .xkm file and load the keymap:

xkbcomp server_0.xkb
xkbcomp server_0.xkm $DISPLAY

In xmodmap this would have something like (untested) keycode 169 = BackSpace, which is of course so simple it needed a better solution.

Tagged , ,

Vim live preview (sort of)

When editing Markdown files or internet drafts in Pandoc's Markdown, I wanted to see some live preview window. I looked around a bit, but the solutions presented on the Internet, seemed to be insufficient, either to clumsy or don't work at all.

My usual routine is: edit -> write -> make -> reload "rendered" file.

Turns out you can automate most of this. Vim has a feature: --servername <id> which allows you to send commands to another vim instance using that <id>. So we need two pieces to make this work.

  1. start a vim instance with --servername markdown and;
  2. some way to send commands to it when building the file, turns out makeprg is an excellent candidate for this, as I was already using Make.

So to start with (2), add an autocmd:

autocmd Filetype pandoc set makeprg=makepandoc\ markdown

Which calls this little shell script:

make && \
vim --servername "${1}" --remote-send '<C-\><C-N>:e<CR>'

And then (1) in a terminal, next to the one you're typing in:

vim -R --servername markdown draft.txt

Now every time you call :make (after writing it), the other vim window reloads it's file. Some things that obviously don't work: scrollbind, so you need to manual scroll to the interesting area of your file.

The fancy stuff right now would be to show a screencast, but ... no.

Tagged , ,

IDN and Private RR in Go DNS

Thanks to the excellent work from Alex Sergeyev, Go DNS has gotten some new features. I want to highlight two: IDN (https://www.ietf.org/rfc/rfc3492.txt) and Private RR support (http://tools.ietf.org/html/rfc6895).


This adds support for converting from and to Punycode. There is no explicit support, you will need to call idn.ToPunycode and idn.FromPunyCode yourself if you are dealing with IDNs.

The examples give in the code:

name := "インターネット.テスト"
fmt.Printf("%s -> %s", name, idn.ToPunycode(name))

Which outputs:

インターネット.テスト -> xn--eckucmux0ukc.xn--zckzah

Private RR

Another thing that was added is the ability to add private RR types: that is new RR type with a code in the 65,280 - 65,534 range. This makes it possible to specify your own (vanity) types, which then work just as the normal ones in Go DNS.

Say for instance I want to add a new RR called "MIEK" (with type code 65281), this RR has two rdata elements: a domain name and it ends with a hexadecimal string. Basically it looks like:

www.example.com.    IN MIEK     example.com. AABB1234

Note that you can also use TYPExxxx and RFC3597 (http://www.ietf.org/rfc/rfc3597.txt) for this, but then the above RR would look something like this:

www.example.com.    IN TYPE65281 \# 8 AABBCCDDAABB1234

Which works, but of course looks bad in the vanity department.

To do this you will need (see example_test.go for example code in the library) create a new record type, implement the PrivateRdata interface and register the new type with the library.

const typeMiek = 65281
type MIEK struct {
    Target string
    Extra  string

Implement PrivateRdata:

  1. String - returns the string presentation of the rdata;
  2. Len - returns the length of the rdata;
  3. Pack and Unpack to convert to and from wire format;
  4. Parse to parse a text presentation of the rdata;
  5. Copy to allow the private RR to be copied inside Go DNS (sometimes needed for DNSSEC).

So lets start with the implementation beginning with String() and Len()

func (m *MIEK) String() string { return m.Target + " " + m.Extra }
func (m *MIEK) Len() int  { return len(m.Target) + len(m.Extra)/2 }

And the functions to convert from and to wire format:

func (m *MIEK) Pack(buf []byte) (int, error) {
        off, err := dns.PackDomainName(m.Target, buf, 0, nil, false)
        if err != nil {
                return off, err
        h, err := hex.DecodeString(m.Extra)
        if err != nil {
                return off, err
        if off+hex.DecodedLen(len(m.Extra)) > len(buf) {
                return len(buf), errors.New("overflow packing hex")
        copy(buf[off:off+hex.DecodedLen(len(m.Extra))], h)
        off += hex.DecodedLen(len(m.Extra))
        return off, nil

func (m *MIEK) Unpack(buf []byte) (int, error) {
        s, off, err := dns.UnpackDomainName(buf, 0)
        if err != nil {
                return len(buf), err
        m.Target = s
        s = hex.EncodeToString(buf[off:])
        m.Extra = s
        return len(buf), nil

And our Parse function that parses the string slice sx into MIEK's rdata.

func (m *MIEK) Parse(sx []string) error {
        if len(sx) < 2 {
                return errors.New("need at least 2 pieces of rdata")
        m.Target = sx[0]
        if  _, ok := dns.IsDomainName(m.Target); !ok {
                return errors.New("bad MIEK Target")
        // Hex data can contain spaces.
        for _, s := range sx[1:] {
                m.Extra += s
        return nil

And Copy which is needed to copy the rdata:

func (m *MIEK) Copy(dest dns.PrivateRdata) error {
        m1, ok := dest.(*MIEK)
        if !ok {
                return dns.ErrRdata
        m1.Target = m.Target
        m1.Extra = m.Extra
        return nil

And finally we can register the new type:

dns.PrivateHandle("MIEK", typeMiek, func() dns.PrivateRdata { return new(MIEK) })
defer dns.PrivateHandleRemove(typeMiek) // when removing again

We can now use all this code to do the following:

m1, _ := dns.NewRR("example.com. IN MIEK target.example.com. DEADBEEF")
log.Printf("%s\n", m1.String())

That prints:

 example.com.   3600    IN  MIEK    target.example.com. DEADBEEF

Mission accomplished :) This is all pretty new code, but I'm pretty happy how this is shaping up, but there might be some changes here in the future.

If you need to export this new type, your best bet is to convert it to an unknown RR:

u := new(dns.RFC3597)                                                                                                                                                                                                  
log.Printf("%s\n", u.String())

example.com.    3600    IN  MIEK    \# 24 06746172676574076578616d706c6503636f6d00deadbeef
Tagged ,

Go DNS package

Go DNS is a package that implements a DNS interface in Go. This library takes a new, innovative and enterprise ready approach sends and receives queries to and from the DNS. It is licensed under the same license as the official Go code, as this is a fork of that code.

The aim is to be powerful, simple and fast.


  • All RR types;
  • Synchronous and asynchronous queries and replies;
  • DNSSEC: validation, signing, key generation, reading .private key files
  • (Fast) sending/receiving/printing packets, RRs;
  • Full control over what is being send;
  • Zone transfers, EDNS0, TSIG, NSID;
  • Server side programming (a full blown nameserver).
  • (Fast) reading zones/RRs from files/strings.


The git repository is hosted on github.

Examples using this library can be found in exdns repository over at github.

Tutorials and more info.

Printing MX records

A small peek in to how to print MX records with Go DNS.

We want to create a little program that prints out the MX records of domains, like so:

% mx miek.nl
miek.nl.        86400   IN      MX      10 elektron.atoom.net.


% mx microsoft.com 
microsoft.com.  3600    IN      MX      10 mail.messaging.microsoft.com.

First the normal header of a Go program, with a bunch of imports. We need the dns package:

package main

import (

Next we need to get the local nameserver to use:

config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")

Then we create a dns.Client to perform the queries for us. In Go:

c := new(dns.Client)

We skip some error handling and assume a zone name is given. So we prepare our question. For that to work, we need:

  1. a new packet (dns.Msg);
  2. setting some header bits;
  3. define a question section;
  4. fill out the question section: os.Args[1] contains the zone name.

Which translates into:

m := new(dns.Msg)
m.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeMX)
m.RecursionDesired = true

Then we need to finally 'ask' the question. We do this by calling the Exchange() function. The unused return value is the rtt (round trip time).

r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))

Check if we got something sane. The following code snippet prints the answer section of the received packet:

Bail out on an error:

if r == nil {
    log.Fatalf("*** error: %s\n", err.Error())

if r.Rcode != dns.RcodeSuccess {
        log.Fatalf(" *** invalid answer name %s after MX query for %s\n", os.Args[1], os.Args[1])

// Stuff must be in the answer section
for _, a := range r.Answer {
        fmt.Printf("%v\n", a)

And we are done.

Full Source

package main

import (

func main() {
    config, _ := dns.ClientConfigFromFile("/etc/resolv.conf")
    c := new(dns.Client)

    m := new(dns.Msg)
    m.SetQuestion(dns.Fqdn(os.Args[1]), dns.TypeMX)
    m.RecursionDesired = true

    r, _, err := c.Exchange(m, net.JoinHostPort(config.Servers[0], config.Port))
    if r == nil {
        log.Fatalf("*** error: %s\n", err.Error())

    if r.Rcode != dns.RcodeSuccess {
            log.Fatalf(" *** invalid answer name %s after MX query for %s\n", os.Args[1], os.Args[1])
    // Stuff must be in the answer section
    for _, a := range r.Answer {
            fmt.Printf("%v\n", a)
Tagged ,

SkyDNS running live

SkyDNS is able to do DNSSEC. It generates signatures and NSEC3 records on the fly. For authenticated denial of existence SkyDNS uses NSEC3 white lies, of course implementing (and testing!) this isn't completely trivial.

To aid in debugging I've setup a live version of SkyDNS on voordeur.atoom.net, under the name the zone http://dnssex.nl:

% dig +mul +noall +answer @voordeur.atoom.net soa skydns.dnssex.nl
skydns.dnssex.nl.    3600 IN SOA ns1.dns.skydns.dnssex.nl. hostmaster.skydns.local. (
                            1403942400 ; serial
                            28800      ; refresh (8 hours)
                            7200       ; retry (2 hours)
                            604800     ; expire (1 week)
                            60         ; minimum (1 minute)

To help getting DNSSEC support 100% working this zone has been delegated and has an DS record in the parent zone. With unbound-host you can see the validation status of this zone:

% unbound-host -C /etc/unbound/unbound.conf -vt SOA skydns.dnssex.nl 
skydns.dnssex.nl has SOA record ns1.dns.skydns.dnssex.nl. hostmaster.skydns.local. 
    1403942400 28800 7200 604800 60 (secure)

Where (secure) indicates DNSSEC is in order.


However getting NXDOMAIN and NODATA response it gets a bit more flaky, but some stuff is working:

% unbound-host -C /etc/unbound/unbound.conf -vt TXT dns.skydns.dnssex.nl
dns.skydns.dnssex.nl has no TXT record (secure)

And some is not:

% unbound-host -C /etc/unbound/unbound.conf -vt SRV server2.miek.skydns.dnssex.nl
Host server2.miek.skydns.dnssex.nl not found: 3(NXDOMAIN). (BOGUS (security failure))
validation failure <server2.miek.skydns.dnssex.nl. SRV IN>: 
    nameerror proof failed from

sadface I believe this is due to defaulting to skydns.dnssex.nl as the closest encloser and *.skydns.dnssex.nl as the source of synthesis, but I haven't had the time to dig deeper into this.


In the near future I hope to update the current test, to include NSEC3 white lies tests.

Tagged , , , , , ,

SkyDNS version 2

SkyDNS version 1 was announced some time ago, since then it has seen some developments, which resulted in SkyDNS version 2. This new version uses Etcd as its backend. This blog post will walk you through the installation and shows how to use it.


SkyDNS(2) is a service discovery tool that utilizes the DNS to find hosts in a distributed environment. But using DNS means "legacy" clients can be used. Want to know if you MariaDB cluster is still up? ping mariadb.skydns.local can be used for that. By default SkyDNS will use skydns.local. as the domain to anchor all names.


If not already installed, install Go for your system, either via the package manager or from source. After that you will need Etcd and SkyDNS:

  • go get github.com/coreos/etcd
  • go get github.com/skynetservices/skydns

After the installation, start Etcd: ./etcd. This will run a lonely, non clusterized Etcd on port 4001 on your local machine. SkyDNS has the ability to use configuration stored in Etcd, but for now we use the command line flags to start SkyDNS:

% ./skydns -addr= -machines= \
[skydns] Jun  8 08:30:19.761 INFO      | ready for queries

Let's see if it works, by using dig:

% dig @ -p 1054 +noall +answer +add SOA skydns.local
skydns.local. 3600 IN SOA ns1.dns.skydns.local. hostmaster.skydns.local. (
                1402210800 ; serial
                28800      ; refresh (8 hours)
                7200       ; retry (2 hours)
                604800     ; expire (1 week)
                60         ; minimum (1 minute)

Somebody is answering! Note in the other examples, I will use the same command line for, but remove all the flags and options. If a query aimed at SkyDNS does not fall under skydns.local. it will forward it to and returns the answer from that:

% dig a miek.nl
miek.nl.        19827 IN A

With this you can configure SkyDNS as your nameserver in /etc/resolv.conf.


The original SkyDNS used a fix naming scheme, environment.service.version.region.skydns.local., SkyDNS2 does away with this, but still it makes sense to defines some scheme to be used in your environment. In this blog post I will use a very simple scheme that only uses a region, like "east", "west", etc.

Let register a service in Etcd, we want the register the name 'web01.east.skydns.local', which listens on port 80 and has an IP4 address of All names used by SkyDNS in Etcd are stored under /skydns/ and we need to reverse the domain name. So to register the name we need to use the key: /v2/keys/skydns/local/skydns/east/web01, the payload of it must be JSON like so:

% curl -XPUT \
    -d value='{"Port":80,"Host": ""}'

And retrieving it via DNS:

% dig A web01.east.skydns.local
web01.east.skydns.local. 3600 IN A

Now we also add another webservice in the east region, web02.east.skydns.local, with IP4 address of

Now suppose you want to have a list of all webservers in the east region? Simple just query for east.skydns.local:

% dig A east.skydns.local
east.skydns.local.  3600 IN A
east.skydns.local.  3600 IN A

Of course IP6 is also supported. Using A and AAAA records allows for "legacy" support, however the port number must be know by the client connection, because that information is not in the returned records. To fix this you can also query for SRV records.

SRV Records

SRV records return much more information than A/AAAA records, it includes a port number, a priority a weight and a name (not an address record). As the service information for web01 only includes an address, SkyDNS will synthesise the SRV record and includes the actual IP address in the additional section:

% dig SRV web01.east.skydns.local
web01.east.skydns.local. 3600 IN SRV 10 100 80 web01.east.skydns.local.
web01.east.skydns.local. 3600 IN A

The numbers "10", "100" and "80" in the SRV records are respectively:

  • 10: priority.
  • 20: weight (when multiple SRV records have the same priority, look at the weight). In SkyDNS weight is a percentage.
  • 80: the port number for the service, if the port is not given in the service, it defaults to 0.

Of course this all works when you query for east.skydns.local as well.


The DNS standards supports wildcards, but SkyDNS extends this usage to allow wildcards within a domainname. To show how this we add another service, this time web01.west.skydns.local. Suppose we want to target all web01 servers? With plain DNS you will need to do two queries (and know about west and east!), with SkyDNS only one is needed:

% dig web01.*.skydns.local
web01.*.skydns.local.   3600    IN  SRV 10 50 80 web01.east.skydns.local.
web01.*.skydns.local.   3600    IN  SRV 10 50 80 web01.west.skydns.local.
web01.east.skydns.local. 3600   IN  A
web01.west.skydns.local. 3600   IN  A


Signed responses are also supported, although authenticated denial of existence based on NSEC3 is a work in progress. A quick primer on how to enable it, as there are a few steps.

  1. Generate a DNSSEC keypair for SkyDNS:

    % dnssec-keygen skydns.local
    Generating key pair........................................++++++ .....++++++ 
  2. Use the basename of the generated key pair as an argument to SkyDNS:

    % ./skydns -addr= -machines= \
        -nameservers= -dnssec Kskydns.local.+005+04821
    [skydns] Jun  8 12:28:37.981 INFO      | ready for queries, signing with Kskydns.local.+005+04821

When you know query with the DO bit on (+dnssec in dig) you will get signed responses:

% dig +dnssec web01.*.skydns.local
web01.*.skydns.local.   3600 IN SRV 10 50 80 web01.east.skydns.local.
web01.*.skydns.local.   3600 IN SRV 10 50 80 web01.west.skydns.local.
web01.*.skydns.local.   3600 IN RRSIG SRV 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     4ixafFhbJSD+Rc4eK764Rberhik/zUtuXDe8kXM= )
web01.east.skydns.local. 3600 IN A
web01.west.skydns.local. 3600 IN A
web01.east.skydns.local. 3600 IN RRSIG A 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     BfPkVwACwBAWaPJWrxy90v43NXdSunl55eUVoP4= )
web01.west.skydns.local. 3600 IN RRSIG A 5 4 3600 (
                     20140615113057 20140608083057 4821 skydns.local.
                     HkbwFHe4Y9qNTF4ygvU0BtObbJ3+e0hW8wr6YIU= )

The signatures are cached, so this does not turn into an easy DDoS at once.

Other responses you expect from a DNS server are supported, like SOA, NS, TXT, etc.

Tagged , , , ,

Learning Go

"Learning Go" is a book that gives an introduction into the Go language of Google. It is licensed under a copy-left license. The book currently consists out +/- 120 (A4 sized) pages and the following chapters:

  1. Introduction
    Show how to install Go and details the lineage of the language Go.
  2. Basics
    Types, variables and control structures.
  3. Functions
    How to make and use functions.
  4. Packages
    Functions and data is grouped together in packages. Here you will see how to make your own package. How to unit test your package is also described.
  5. Beyond the basics
    Learn how to create your own data types and define function on them (called methods in Go).
  6. Interfaces
    Go does not support Object Orientation in the traditional sense. In Go the central concept is interfaces.
  7. Concurrency
    With the go keyword function can be started in separate routines (called goroutines). Communication with those goroutines is done via channels.
  8. Communication
    How to create/read/write from and to files. And how to do networking.

Each chapter concludes with a number of exercises (and answers) to may help you to get some hands on experience. Currently it has more than 30 exercises.

There is also a Chinese translation by Mike Spook.

What readers say:

I am really glad that I found your Go book. It's been a couple of weeks since I started learning Go, but didn't make much progress till I found your book.

I also read with great interest the (successive versions of the) free E-book by Miek Gieben & Co. Which I find definitely very well crafted and very useful. Definitely an extremely laudable initiative.

Prebuild PDFs can be found at /downloads/Go. The source code of the book can be found on github.

It is written in LaTeX with the Memoir class (and a bunch a extra classes).

Questions, patches, text, bug reports and general discussions can be directed to miek@miek.nl (in English or Dutch). If you like this work you may choose support it by sending me money :)

Tagged , , ,