summaryrefslogtreecommitdiff
path: root/app/BahnhofDNS.hs
blob: 2111aa0171d76565eda5811b35c300c166578570 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
module Main where

import           Data.ByteString
import           Data.Text
import           Data.Text.Encoding as TSE
import           Network.DNS
import           Network.Socket
import           Util

main :: IO ()
main = do -- general structure like sample code at https://book.realworldhaskell.org/read/sockets-and-syslog.html (for UDP server)
  staticData <- readData
  addr : _ <- getAddrInfo (Just defaultHints { addrSocketType = Datagram, addrFamily = AF_INET6, addrFlags = [AI_PASSIVE] }) Nothing (Just "5300")
  sckt <- openSocket addr
  bind sckt (addrAddress addr)
  putStrLn "Starting DNS server (listening on port 5300)"
  procMessages staticData sckt
  where
    procMessages staticData sckt = do
      (m,ad) <- receiveFrom sckt -- Network.DNS

      sendTo sckt (response m) ad
      procMessages staticData sckt
      where
        response m = encode $ case (question m) of
          (Question qh TXT) : _ -> queryAnswer (uid m) qh
          _                     -> m -- ping back question without answering it
                                     -- no other way to generate empty responses without providing a question in this library?

        queryAnswer oid qh = case maybeAnswer qh of
          Just answer -> responseTXT   oid (Question qh TXT) (TSE.encodeUtf8 answer)
          Nothing     -> responseEmpty oid (Question qh TXT)

        uid msg = identifier (header msg)
        maybeAnswer qh = case queryFromQuestionHead qh of
          (q, "RIL100")    -> lookupCode (Ril100 q) (ril100map staticData)
          (q, "LEITPUNKT") -> lookupName q (leitpunktMap staticData)
                              >>= (`lookupCode` (ril100map staticData))
          (_, _) -> Nothing

queryFromQuestionHead :: ByteString -> (Text, String)
queryFromQuestionHead q = case splt of
  quest : questType : _ -- here we could add enforcement that we only query the intended namespace and not arbitrary subspaces (nsst.ril100.a.b.c.d.e.…)
    -> (quest, Data.Text.unpack questType)
  _                     -> (Data.Text.empty, "") -- side question: is a query without a dot even a legal DNS query?
  where
    splt = splitOn dot (toUpper $ TSE.decodeUtf8 q)
    dot = Data.Text.pack "."

responseEmpty :: Identifier -> Question -> DNSMessage
responseEmpty idt q = makeResponse idt q []

responseTXT :: Identifier -> Question -> ByteString -> DNSMessage
responseTXT idt q txt = makeResponse idt q [ans]
  where
    dom = qname q
    ans = ResourceRecord dom TXT classIN 604800 (RD_TXT txt)