Tag Archives: go-lang

Scratching my own itch, or how to publish multicast DNS records in Go

DHCP handles allocating IP addresses to the devices on my home network, but that leaves me with the problem of mapping IP addresses to names. The Macs and Linux hosts sort this out with Bonjour and Avahi, but annoyingly two devices, my router and my NAS don’t support it.

Now, I could hack my /etc/hosts file for those two hosts, but that doesn’t work well for embedded devices like iPad. Alternatively, I could install Avahi on my NAS, which is running Debian Sid armel, but space on it’s tiny root file system is always at a premium.

Fortunately, my NAS is also one of my Go development systems, so after some prodding from +Brian Ketelsen, I wrote a package to replicate the parts of Avahi that I needed. Here it is

https://github.com/davecheney/mdns/

Here is a simple example of how to use the package.

import (
        "net"
        "github.com/davecheney/mdns"
)

func main() {
        // Publish a SVR record for ssh running on port 22 for my home NAS.

        // Publish an A record for the host
        mdns.PublishA("stora.local.", 3600, net.IPv4(192, 168, 1, 200))

        // Publish a PTR record for the _ssh._tcp DNS-SD type
        mdns.PublishPTR("_ssh._tcp.local.", 3600, "stora._ssh._tcp.local.")

        // Publish a SRV record typing the _ssh._tcp record to an A record and a port.
        mdns.PublishSRV("stora._ssh._tcp.local.", 3600, "stora.local.", 22)

        // Most mDNS browsing tools expect a TXT record for the service even if there
        // are not records defined by RFC 2782.
        mdns.PublishTXT("stora._ssh._tcp.local.", 3600, "")

        select{}
}

So that’s it. This package has scratched my itch and I hope it is useful for others. I’d love to hear feedback and suggestions you’ve found it to be useful.

updated 16/10/2011 Thanks to @eneff for the select{} tip.

Netgear Stora as an ARM development platform

About a week ago I posted a request for recommendations for ARM based systems that could be used for Go development. There were some great responses, including the BeagleBoard and the Guru Plug. Being impatient, and in Australia, I ended up getting a Netgear Stora which has turned out to be a great home NAS, and a capable ARM5 development system. This is the same hardware, albeit with less RAM, that ships in the ShivaPlug.

axentraserver(~/go/src) % export MAKEFLAGS=-j1
axentraserver(~/go/src) % hg identify 
546b1fc95dcc+ tip
axentraserver(~/go/src) % time ./make.bash > /dev/null
hg not installed
conflicts: 3 shift/reduce

real    10m48.889s
user    9m15.380s
sys     0m52.480s

Not too shabby, my 8g host (2.8Ghz Celeron) turns around the same build in just under 12 minutes.

Pros

  • Very good value. For less thatn $200 AUD you get a 1.2Ghz Marvel ARM5 CPU, 128mb of ram and a 1Tb Seagate 3.5″ drive (and a slot for a second drive). Online Computer have the 1Tb units for $185.
  • Very hackable. SSH is enabled out of the box, if you know the magic suffix that Netgear, and all users created via the web interface are in /etc/sudoers. The fantastic ipkg system will close the gap between the slimmed down RedHat distribution that Netgear Axentra ship and a GNU buildchain that can bootstrap Go.

Cons

  • 128mb of ram, non expandable. This actually turns out to not be a big deal. The stock install has ~75mb of RAM free while running. Turing off a few options and trimming the daemons Netgear installs can get another 10-15mb back.

How to dial remote SSL/TLS services in Go

Go has wonderful support for server side TLS, but the client side takes a little bit of massaging to generate a correct Config struct. Check out this thread from Hans Stimer for more details on handling TLS on the server side.

package main

import (
	"crypto/tls"
	"io/ioutil"
	"net"
	"os"
	"time"
)

func loadRootCA(file string) (*tls.CASet, os.Error) {
	pemBytes, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}

	caset := tls.NewCASet()
	if caset.SetFromPEM(pemBytes) {
		return caset, nil
	}
	return nil, os.NewError("Unable to decode root CA set")
}

func newClientConfig(rootCAPath string) (*tls.Config, os.Error) {
	rootca, err := loadRootCA(rootCAPath)
	if err != nil {
		return nil, err
	}

	urandom, err := os.Open("/dev/urandom", os.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}

	return &tls.Config{
		Rand:    urandom,
		Time:    time.Seconds,
		RootCAs: rootca,
	}, nil
}

func dialTLS(raddr *net.TCPAddr) (c *tls.Conn, err os.Error) {
	config, err := newClientConfig("ca-certificates.crt")
	if err != nil {
		return nil, err
	}

	conn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		return nil, err
	}

	c = tls.Client(conn, config)
	err = c.Handshake()
	if err == nil {
		return c, nil
	}
	c.Close()
	return nil, err
}