DNS и Go

8 minute read

Перевод статьи "Go DNS package".

Пакет Go DNS реализует интерфейс для управления DNS с помощью Go. По сути, это либа которая позволяет делать и принимать DNS запросы. Весь код лицензируется так же как и код самого Go.

Основная цель создания этого инструмента - предоставить простой и мощный инструмент.

Что поддерживает эта библиотека:

  • Все типы RR;
  • Синхронные и асинхронные запросы и ответы:
  • DNSSEC - валидация, подписи, генерация ключей, чтение файлов .private:
  • (Скоро) отправка/получение/отображение пакетов и RR;
  • Полный контроль в буквальном смысле:
  • Перенос зон, EDNS0, TSIG, NSID;
  • Возможности полноценного сервера имен:
  • (Скоро) чтение зон/RR из файлов и строк:

Код

Сам код пакета находится на github.

Примеры использования пакета можно найти в специальном пакете, все на том же github.

Чуть больше о использовании

Вывод MX записей

Маленький лайвхак, как выводить MX записи используя Go DNS.

Попробуем написать простую программу, которая отображает MX записи для домена. Это будет выглядеть вот так:

% 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.

Начнем со списка используемых пакетов:

package main

import (
    "os"
    "net"
    "fmt"
    "log"

    "github.com/miekg/dns"
)

Теперь мы можем создать небольшой локальный сервер имен:

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

Теперь создадим клиент dns.Client для выполнения наших запросов:

c := new(dns.Client)

В этом примере мы пропустили обработку ошибок и считаем, что зона уже указана. Для продолжения нам нужно еще несколько вещей:

  • создать пакет экземпляр пакета((dns.Msg));
  • установить некоторые заголовочные биты;
  • указать секцию запроса;
  • заполнить секцию запроса: считаем, что в os.Args[1] содержится имя зоны:

В итоге получится вот так:

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

В конце всего мы должны выполнить наш запрос. Для этого нужно вызвать метод Exchange(). Пропущенное возвращенное значение - это rtt (round trip time).

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])
}

// Весь результат должен быть в поле Answer
for _, a := range r.Answer {
        fmt.Printf("%v\n", a)
}

И на этом, собственно, все. Полный код примера:

package main

import (
    "net"
    "os"
    "log"
    "fmt"

    "github.com/miekg/dns"
)

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])
    }
    // Весь результат должен быть в поле Answer
    for _, a := range r.Answer {
            fmt.Printf("%v\n", a)
    }
}