123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- package server
- import (
- "net"
- "os"
- "os/signal"
- "strings"
- "sync"
- "time"
- "git.giaever.org/joachimmg/go-log.git/log"
- "git.giaever.org/joachimmg/m-dns/config"
- "git.giaever.org/joachimmg/m-dns/errors"
- "git.giaever.org/joachimmg/m-dns/zone"
- "github.com/miekg/dns"
- )
- type MDnsServer struct {
- sync.Mutex
- zone.Zone
- *net.Interface
- ipv4 *net.UDPConn
- ipv6 *net.UDPConn
- running bool
- runCh chan struct{}
- }
- func New(z zone.Zone, iface *net.Interface) (*MDnsServer, error) {
- m := new(MDnsServer)
- if m == nil {
- log.Traceln(errors.Server, errors.OutOfMemory)
- return nil, errors.OutOfMemory
- }
- if z == nil {
- log.Traceln(errors.Server, errors.ServerIsNil)
- return nil, errors.ServerIsNil
- }
- m.Zone = z
- m.Interface = iface
- var err error
- if m.ipv4, err = net.ListenMulticastUDP("udp4", m.Interface, config.MdnsIPv4Addr); err != nil {
- log.Infoln(errors.Server, config.MdnsIPv4, err)
- }
- if m.ipv6, err = net.ListenMulticastUDP("udp6", m.Interface, config.MdnsIPv6Addr); err != nil {
- log.Infoln(errors.Server, config.MdnsIPv6, err)
- }
- if m.ipv4 == nil && m.ipv6 == nil {
- log.Traceln(errors.Server, errors.ServerNoListenersStarted)
- return nil, errors.ServerNoListenersStarted
- }
- m.runCh = make(chan struct{})
- m.running = true
- go m.recv(m.ipv4)
- go m.recv(m.ipv6)
- return m, nil
- }
- func (m *MDnsServer) shutdownListener() {
- log.Traceln("Shutdown listener set on ctrl+x")
- c := make(chan os.Signal, 1)
- signal.Notify(c, os.Interrupt)
- go func() {
- for range c {
- m.Shutdown()
- }
- }()
- }
- func (m *MDnsServer) Shutdown() {
- log.Traceln("Shutting down MDNS-server")
- m.Lock()
- defer m.Unlock()
- if !m.running {
- return
- }
- if m.ipv4 != nil {
- m.ipv4.Close()
- }
- if m.ipv6 != nil {
- m.ipv6.Close()
- }
- m.running = false
- close(m.runCh)
- }
- func (m *MDnsServer) Daemon() {
- log.Traceln("Daemon running.")
- go m.shutdownListener()
- <-m.runCh
- log.Traceln("Daemon ending.")
- }
- func (m *MDnsServer) recv(i *net.UDPConn) {
- if i == nil {
- return
- }
- buf := make([]byte, config.BufSize)
- for m.running {
- m.Lock()
- i.SetReadDeadline(time.Now().Add(config.BufReadDeadline * time.Second))
- _, addr, err := i.ReadFromUDP(buf)
- m.Unlock()
- if err != nil {
- if !strings.Contains(err.Error(), "i/o timeout") {
- log.Traceln(errors.Server, err)
- }
- continue
- }
- go m.handlePacket(buf, addr)
- }
- }
- func (m *MDnsServer) send(msg *dns.Msg, addr *net.UDPAddr) (int, error) {
- if msg == nil {
- return 0, nil
- }
- buf, err := msg.Pack()
- if err != nil {
- return 0, err
- }
- if addr.IP.To4() != nil {
- return m.ipv4.WriteToUDP(buf, addr)
- }
- return m.ipv6.WriteToUDP(buf, addr)
- }
- func (m *MDnsServer) handlePacket(p []byte, addr *net.UDPAddr) {
- msg := new(dns.Msg)
- if err := msg.Unpack(p); err != nil {
- log.Warningln(errors.Server, addr, err)
- }
- umsg, mmsg, err := m.handleMsg(msg)
- if err != nil {
- log.Warningln(errors.Server, addr, err)
- return
- }
- if n, err := m.send(umsg, addr); err != nil {
- log.Warningln(errors.Server, "Wrote", n, err)
- }
- if n, err := m.send(mmsg, addr); err != nil {
- log.Warningln(errors.Server, "Wrote", n, err)
- }
- }
- func (m *MDnsServer) handleMsg(msg *dns.Msg) (*dns.Msg, *dns.Msg, error) {
- if msg.Opcode != dns.OpcodeQuery {
- log.Traceln(errors.Server, errors.ServerReceivedNonQueryOpcode)
- return nil, nil, errors.ServerReceivedNonQueryOpcode
- }
- if msg.Rcode != 0 {
- log.Traceln(errors.Server, errors.ServerReceivedNonZeroRcode)
- return nil, nil, errors.ServerReceivedNonZeroRcode
- }
- if msg.Truncated {
- log.Traceln(errors.Server, errors.ServerReceivedTruncatedSet)
- return nil, nil, errors.ServerReceivedTruncatedSet
- }
- var uAnswer, mAnswer []dns.RR
- for _, q := range msg.Question {
- uRecords, mRecords := m.handleQuestion(q)
- uAnswer = append(uAnswer, uRecords...)
- mAnswer = append(mAnswer, mRecords...)
- }
- return m.handleResponse(msg, true, uAnswer), m.handleResponse(msg, false, mAnswer), nil
- }
- func (m *MDnsServer) handleQuestion(question dns.Question) ([]dns.RR, []dns.RR) {
- r := m.Records(question)
- if len(r) == 0 {
- return nil, nil
- }
- for i, rec := range r {
- log.Traceln(errors.Server, "Record", i, rec)
- }
- if question.Qclass&(1<<15) != 0 || config.ForceUnicast {
- return r, nil
- }
- return nil, r
- }
- func (m *MDnsServer) handleResponse(msg *dns.Msg, uni bool, ans []dns.RR) *dns.Msg {
- id := uint16(0)
- if uni {
- id = msg.Id
- }
- if len(ans) == 0 {
- return nil
- }
- return &dns.Msg{
- MsgHdr: dns.MsgHdr{
- Id: id,
- Response: true,
- Opcode: dns.OpcodeQuery,
- Authoritative: true,
- },
- Compress: true,
- Answer: ans,
- }
- }
|