Adding this to the tool for future use and viewing
diff --git a/mdns.go b/mdns.go
new file mode 100644
index 0000000..81efa7f
--- /dev/null
+++ b/mdns.go
@@ -0,0 +1,263 @@
+package main
+
+import "net"
+import "strings"
+import "encoding/hex"
+import "encoding/binary"
+//import "time"
+import "errors"
+import "log"
+import "os"
+
+type DNSHeader struct {
+ ID uint16
+ Flags uint16
+ QCount uint16
+ ACount uint16
+ NSCount uint16
+ ARCount uint16
+}
+
+type MDNSAnswer struct {
+ Header *DNSHeader
+ Domain string
+ Data net.IP
+}
+
+type MDNSQuery struct {
+ Header *DNSHeader
+ Domain string
+ Type uint16
+ Class uint16
+ Unicast bool
+}
+
+const AAAA uint16 = 28
+
+func buildHeader(header DNSHeader) []byte {
+ out := make([]byte, 12)
+ binary.BigEndian.PutUint16(out[0:2], header.ID)
+ binary.BigEndian.PutUint16(out[2:4], header.Flags)
+ binary.BigEndian.PutUint16(out[4:6], header.QCount)
+ binary.BigEndian.PutUint16(out[6:8], header.ACount)
+ binary.BigEndian.PutUint16(out[8:10], header.NSCount)
+ binary.BigEndian.PutUint16(out[10:12], header.ARCount)
+ return out
+}
+
+func buildDomain(domain string) []byte {
+ var out []byte
+ parts := strings.Split(domain, ".")
+ for _, dpart := range parts {
+ ascii := []byte(dpart)
+ out = append(out, byte(len(ascii)))
+ out = append(out, ascii...)
+ }
+ out = append(out, byte(0))
+ return out
+}
+
+func buildQuery(domain string) []byte {
+ header := buildHeader(DNSHeader{QCount: 1})
+ domainBytes := buildDomain(domain)
+ payload := append(header, domainBytes...)
+ payload = append(payload, []byte{0, byte(AAAA)}...) //QTYPE=AAAA
+ payload = append(payload, []byte{1 << 7, 1}...) // Unicast=true QCLASS=1
+ return payload
+}
+
+func buildResponse(domain string, ip net.IP) []byte {
+ header := buildHeader(DNSHeader{ACount: 1})
+ domainBytes := buildDomain(domain)
+ payload := append(header, domainBytes...)
+ payload = append(payload, []byte{0, byte(AAAA)}...) //QTYPE=AAAA
+ payload = append(payload, []byte{0, 1}...) // FLUSH=false, RRCLASS=1
+ payload = append(payload, []byte{0, 0, 0, 1}...) // Cache for 1 second
+ payload = append(payload, []byte{0, 16}...) // IPV6 IP is 16 bytes long
+ payload = append(payload, ip...)
+ return payload
+}
+
+func parseShort(buff []byte) (uint16, []byte, error) {
+ if len(buff) < 2 {
+ return 0, nil, errors.New("buffer to short for uint16")
+ }
+ return binary.BigEndian.Uint16(buff[0:2]), buff[2:], nil
+}
+
+func parseHeader(buff []byte) (*DNSHeader, []byte, error) {
+ // If we can't read the header return
+ if len(buff) < 12 {
+ return nil, nil, errors.New("buffer to short for DNS header")
+ }
+
+ // Read the header information
+ header := new(DNSHeader)
+ header.ID, buff, _ = parseShort(buff)
+ header.Flags, buff, _ = parseShort(buff)
+ header.QCount, buff, _ = parseShort(buff)
+ header.ACount, buff, _ = parseShort(buff)
+ header.NSCount, buff, _ = parseShort(buff)
+ header.ARCount, buff, _ = parseShort(buff)
+
+ return header, buff, nil
+}
+
+func parseDomain(buff []byte) (string, []byte, error) {
+ var domain []string
+ i := 0
+ for i < len(buff) && buff[i] != 0 {
+ size := int(buff[i])
+ i += 1
+ domain = append(domain, string(buff[i:i+size]))
+ i += int(size)
+ }
+ if i >= len(buff) {
+ return "", nil, errors.New("domain string was invalid")
+ }
+ return strings.Join(domain, "."), buff[i+1:], nil
+}
+
+func parseQuery(buff []byte) (*MDNSQuery, error) {
+ out := new(MDNSQuery)
+ var err error
+ out.Header, buff, err = parseHeader(buff)
+ if err != nil {
+ return nil, err
+ }
+ if out.Header.QCount != 1 {
+ return nil, errors.New("header indicates that this is not a query")
+ }
+ out.Domain, buff, err = parseDomain(buff)
+ if err != nil {
+ return nil, err
+ }
+ if len(buff) < 4 {
+ return nil, errors.New("buffer too short for query")
+ }
+ out.Type, buff, err = parseShort(buff)
+ if err != nil {
+ return nil, err
+ }
+ tmp, buff, _ := parseShort(buff)
+ if err != nil {
+ return nil, err
+ }
+ out.Unicast = (tmp & 1) != 0
+ out.Class = tmp >> 1
+ return out, nil
+}
+
+func parseAnswer(buff []byte) (*MDNSAnswer, error) {
+ header, buff, err := parseHeader(buff)
+ if err != nil {
+ return nil, err
+ }
+ if header.ACount != 1 {
+ return nil, errors.New("header indicates that this is not an answer")
+ }
+ domain, buff, err := parseDomain(buff)
+ if err != nil {
+ return nil, err
+ }
+ if len(buff) < 11 {
+ return nil, errors.New("buffer too short for answer")
+ }
+ // We want to skip some fields that we don't care about
+ buff = buff[8:]
+ // Now we should be at RDLENGTH which is the length of the data response
+ shortLen, buff, _ := parseShort(buff)
+
+ dataLength := int(shortLen)
+ // Now read the data
+ data := make([]byte, dataLength)
+ copy(data, buff[:dataLength])
+ out := new(MDNSAnswer)
+ out.Header = header
+ out.Domain = domain
+ out.Data = data
+ // Finally return the thing
+ return out, nil
+}
+
+// Issues:
+// 1) assumes "domain" is ascii and shortish
+// 2) iface must be specified by user which seems wrong
+// 3) If it dosn't get an answer it infinite loops
+// According to the standard the port should be 5353 but I allow to it be
+// anything here.
+// TODO: Make this async in someway that allows the user to handle timeouts.
+func mdnsQuery(logger *log.Logger, domain string, port int, iface string) (*MDNSAnswer, error) {
+ ip := net.ParseIP("ff02::fb")
+ addr := net.UDPAddr{ip, port, iface}
+ conn, err := net.ListenUDP("udp6", &addr)
+ if err != nil {
+ return nil, err
+ }
+ defer conn.Close()
+ // Send the request
+ conn.WriteTo(buildQuery(domain), &addr)
+ // Read the result into a UDPPacket
+ for {
+ buff := make([]byte, 1024)
+ n, _, _ := conn.ReadFrom(buff)
+ buff = buff[:n]
+ response, err := parseAnswer(buff)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ } else {
+ if response.Header.ACount != 1 {
+ logger.Printf("Non-answer Data:\n%s", err, hex.Dump(buff))
+ // This is not an answer to a oneshot mDNS query
+ continue
+ }
+ if response.Domain == domain {
+ logger.Printf("Received answer: %s -> %s\n", domain, response.Data.String())
+ return response, nil
+ }
+ }
+ }
+}
+
+func mdnsServer(logger *log.Logger, domain string, port int, selfip string) error {
+ selfIP := net.ParseIP(selfip)
+ ip := net.ParseIP("ff02::fb")
+ addr := net.UDPAddr{IP: ip, Port: 5353}
+ conn, err := net.ListenMulticastUDP("udp6", nil, &addr)
+ if err != nil {
+ return err
+ }
+ for {
+ buff := make([]byte, 1024)
+ n, addr, err := conn.ReadFrom(buff)
+ if err != nil {
+ logger.Printf("Error: %s\n", err)
+ continue
+ }
+ buff = buff[:n]
+ query, err := parseQuery(buff)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ } else {
+ logger.Printf("%s wants to know %s", addr, query.Domain)
+ if domain == query.Domain {
+ response := buildResponse(domain, selfIP)
+ _, err := conn.WriteTo(response, addr)
+ logger.Printf("%s was told %s\n", addr, selfIP)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ }
+ }
+ }
+ }
+}
+
+func main() {
+ flags := log.Ltime | log.Lmicroseconds | log.LUTC
+ logger := log.New(os.Stdout, "", flags)
+ mdnsServer(logger, "blarg.local", 5353, "fe80::508c:b874:8526:40bd")
+ //for {
+ //mdnsQuery(logger, "blarg.local", 5353, "eth0")
+ //time.Sleep(5 * 1000 * 1000 * 1000) // Sleep 5 seconds
+ //}
+}
diff --git a/mdns/mdns.go b/mdns/mdns.go
new file mode 100644
index 0000000..71d022c
--- /dev/null
+++ b/mdns/mdns.go
@@ -0,0 +1,264 @@
+package main
+
+import "net"
+import "strings"
+import "encoding/hex"
+import "encoding/binary"
+
+//import "time"
+import "errors"
+import "log"
+import "os"
+
+type DNSHeader struct {
+ ID uint16
+ Flags uint16
+ QCount uint16
+ ACount uint16
+ NSCount uint16
+ ARCount uint16
+}
+
+type MDNSAnswer struct {
+ Header *DNSHeader
+ Domain string
+ Data net.IP
+}
+
+type MDNSQuery struct {
+ Header *DNSHeader
+ Domain string
+ Type uint16
+ Class uint16
+ Unicast bool
+}
+
+const AAAA uint16 = 28
+
+func buildHeader(header DNSHeader) []byte {
+ out := make([]byte, 12)
+ binary.BigEndian.PutUint16(out[0:2], header.ID)
+ binary.BigEndian.PutUint16(out[2:4], header.Flags)
+ binary.BigEndian.PutUint16(out[4:6], header.QCount)
+ binary.BigEndian.PutUint16(out[6:8], header.ACount)
+ binary.BigEndian.PutUint16(out[8:10], header.NSCount)
+ binary.BigEndian.PutUint16(out[10:12], header.ARCount)
+ return out
+}
+
+func buildDomain(domain string) []byte {
+ var out []byte
+ parts := strings.Split(domain, ".")
+ for _, dpart := range parts {
+ ascii := []byte(dpart)
+ out = append(out, byte(len(ascii)))
+ out = append(out, ascii...)
+ }
+ out = append(out, byte(0))
+ return out
+}
+
+func buildQuery(domain string) []byte {
+ header := buildHeader(DNSHeader{QCount: 1})
+ domainBytes := buildDomain(domain)
+ payload := append(header, domainBytes...)
+ payload = append(payload, []byte{0, byte(AAAA)}...) //QTYPE=AAAA
+ payload = append(payload, []byte{1 << 7, 1}...) // Unicast=true QCLASS=1
+ return payload
+}
+
+func buildResponse(domain string, ip net.IP) []byte {
+ header := buildHeader(DNSHeader{ACount: 1})
+ domainBytes := buildDomain(domain)
+ payload := append(header, domainBytes...)
+ payload = append(payload, []byte{0, byte(AAAA)}...) //QTYPE=AAAA
+ payload = append(payload, []byte{0, 1}...) // FLUSH=false, RRCLASS=1
+ payload = append(payload, []byte{0, 0, 0, 1}...) // Cache for 1 second
+ payload = append(payload, []byte{0, 16}...) // IPV6 IP is 16 bytes long
+ payload = append(payload, ip...)
+ return payload
+}
+
+func parseShort(buff []byte) (uint16, []byte, error) {
+ if len(buff) < 2 {
+ return 0, nil, errors.New("buffer to short for uint16")
+ }
+ return binary.BigEndian.Uint16(buff[0:2]), buff[2:], nil
+}
+
+func parseHeader(buff []byte) (*DNSHeader, []byte, error) {
+ // If we can't read the header return
+ if len(buff) < 12 {
+ return nil, nil, errors.New("buffer to short for DNS header")
+ }
+
+ // Read the header information
+ header := new(DNSHeader)
+ header.ID, buff, _ = parseShort(buff)
+ header.Flags, buff, _ = parseShort(buff)
+ header.QCount, buff, _ = parseShort(buff)
+ header.ACount, buff, _ = parseShort(buff)
+ header.NSCount, buff, _ = parseShort(buff)
+ header.ARCount, buff, _ = parseShort(buff)
+
+ return header, buff, nil
+}
+
+func parseDomain(buff []byte) (string, []byte, error) {
+ var domain []string
+ i := 0
+ for i < len(buff) && buff[i] != 0 {
+ size := int(buff[i])
+ i += 1
+ domain = append(domain, string(buff[i:i+size]))
+ i += int(size)
+ }
+ if i >= len(buff) {
+ return "", nil, errors.New("domain string was invalid")
+ }
+ return strings.Join(domain, "."), buff[i+1:], nil
+}
+
+func parseQuery(buff []byte) (*MDNSQuery, error) {
+ out := new(MDNSQuery)
+ var err error
+ out.Header, buff, err = parseHeader(buff)
+ if err != nil {
+ return nil, err
+ }
+ if out.Header.QCount != 1 {
+ return nil, errors.New("header indicates that this is not a query")
+ }
+ out.Domain, buff, err = parseDomain(buff)
+ if err != nil {
+ return nil, err
+ }
+ if len(buff) < 4 {
+ return nil, errors.New("buffer too short for query")
+ }
+ out.Type, buff, err = parseShort(buff)
+ if err != nil {
+ return nil, err
+ }
+ tmp, buff, _ := parseShort(buff)
+ if err != nil {
+ return nil, err
+ }
+ out.Unicast = (tmp & 1) != 0
+ out.Class = tmp >> 1
+ return out, nil
+}
+
+func parseAnswer(buff []byte) (*MDNSAnswer, error) {
+ header, buff, err := parseHeader(buff)
+ if err != nil {
+ return nil, err
+ }
+ if header.ACount != 1 {
+ return nil, errors.New("header indicates that this is not an answer")
+ }
+ domain, buff, err := parseDomain(buff)
+ if err != nil {
+ return nil, err
+ }
+ if len(buff) < 11 {
+ return nil, errors.New("buffer too short for answer")
+ }
+ // We want to skip some fields that we don't care about
+ buff = buff[8:]
+ // Now we should be at RDLENGTH which is the length of the data response
+ shortLen, buff, _ := parseShort(buff)
+
+ dataLength := int(shortLen)
+ // Now read the data
+ data := make([]byte, dataLength)
+ copy(data, buff[:dataLength])
+ out := new(MDNSAnswer)
+ out.Header = header
+ out.Domain = domain
+ out.Data = data
+ // Finally return the thing
+ return out, nil
+}
+
+// Issues:
+// 1) assumes "domain" is ascii and shortish
+// 2) iface must be specified by user which seems wrong
+// 3) If it dosn't get an answer it infinite loops
+// According to the standard the port should be 5353 but I allow to it be
+// anything here.
+// TODO: Make this async in someway that allows the user to handle timeouts.
+func mdnsQuery(logger *log.Logger, domain string, port int, iface string) (*MDNSAnswer, error) {
+ ip := net.ParseIP("ff02::fb")
+ addr := net.UDPAddr{ip, port, iface}
+ conn, err := net.ListenUDP("udp6", &addr)
+ if err != nil {
+ return nil, err
+ }
+ defer conn.Close()
+ // Send the request
+ conn.WriteTo(buildQuery(domain), &addr)
+ // Read the result into a UDPPacket
+ for {
+ buff := make([]byte, 1024)
+ n, _, _ := conn.ReadFrom(buff)
+ buff = buff[:n]
+ response, err := parseAnswer(buff)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ } else {
+ if response.Header.ACount != 1 {
+ logger.Printf("Non-answer Data:\n%s", err, hex.Dump(buff))
+ // This is not an answer to a oneshot mDNS query
+ continue
+ }
+ if response.Domain == domain {
+ logger.Printf("Received answer: %s -> %s\n", domain, response.Data.String())
+ return response, nil
+ }
+ }
+ }
+}
+
+func mdnsServer(logger *log.Logger, domain string, port int, selfip string) error {
+ selfIP := net.ParseIP(selfip)
+ ip := net.ParseIP("ff02::fb")
+ addr := net.UDPAddr{IP: ip, Port: 5353}
+ conn, err := net.ListenMulticastUDP("udp6", nil, &addr)
+ if err != nil {
+ return err
+ }
+ for {
+ buff := make([]byte, 1024)
+ n, addr, err := conn.ReadFrom(buff)
+ if err != nil {
+ logger.Printf("Error: %s\n", err)
+ continue
+ }
+ buff = buff[:n]
+ query, err := parseQuery(buff)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ } else {
+ logger.Printf("%s wants to know %s", addr, query.Domain)
+ if domain == query.Domain {
+ response := buildResponse(domain, selfIP)
+ _, err := conn.WriteTo(response, addr)
+ logger.Printf("%s was told %s\n", addr, selfIP)
+ if err != nil {
+ logger.Printf("Error: %s\nData:\n%s", err, hex.Dump(buff))
+ }
+ }
+ }
+ }
+}
+
+func main() {
+ flags := log.Ltime | log.Lmicroseconds | log.LUTC
+ logger := log.New(os.Stdout, "", flags)
+ mdnsServer(logger, "blarg.local", 5353, "fe80::508c:b874:8526:40bd")
+ //for {
+ //mdnsQuery(logger, "blarg.local", 5353, "eth0")
+ //time.Sleep(5 * 1000 * 1000 * 1000) // Sleep 5 seconds
+ //}
+}