summaryrefslogtreecommitdiff
path: root/ymir
diff options
context:
space:
mode:
Diffstat (limited to 'ymir')
-rw-r--r--ymir/mlmmj-expose.hs182
-rw-r--r--ymir/mlmmj-expose.nix112
-rw-r--r--ymir/zones/email.nights.soa34
-rw-r--r--ymir/zones/index.nix3
-rw-r--r--ymir/zones/li.kleen.soa (renamed from ymir/zones/li.lmu.soa)17
5 files changed, 245 insertions, 103 deletions
diff --git a/ymir/mlmmj-expose.hs b/ymir/mlmmj-expose.hs
new file mode 100644
index 00000000..f074659b
--- /dev/null
+++ b/ymir/mlmmj-expose.hs
@@ -0,0 +1,182 @@
1{-# LANGUAGE ViewPatterns, RecordWildCards, OverloadedStrings #-}
2
3import System.IO
4import System.IO.Error
5import System.FilePath
6import System.Environment
7import System.Exit
8import System.Directory
9import System.Process
10import Text.Printf
11
12import Data.Char
13
14import Control.Monad
15
16import Crypto.Hash
17
18import qualified Data.ByteString.Lazy as LBS
19import qualified Data.ByteString.Char8 as CBS
20
21import qualified Data.UUID as UUID (toString)
22import qualified Data.UUID.V4 as UUID (nextRandom)
23
24import Data.Aeson
25import Data.Aeson.Encode.Pretty
26
27import Data.Set (Set)
28import qualified Data.Set as Set
29
30newtype FoxReplace = FoxReplace (Set FoxReplaceGroup)
31 deriving (Ord, Eq, Show)
32
33data FoxReplaceGroup = FoxReplaceGroup
34 { groupName :: String
35 , groupUrls :: Set String
36 , groupSubs :: Set FoxReplaceSub
37 , groupHtmlMode :: FoxReplaceHTML
38 }
39 deriving (Ord, Eq, Show)
40
41data FoxReplaceHTML = NoHTML | OutputOnlyHTML | BothHTML
42 deriving (Ord, Eq, Enum, Show)
43
44data FoxReplaceSub = FoxReplaceSub
45 { rInput, rOutput :: String
46 , rInputType :: SubInput
47 , rCaseSensitive :: Bool
48 }
49 deriving (Ord, Eq, Show)
50
51data SubInput = TextInput | WordInput | RegexpInput
52 deriving (Ord, Eq, Enum, Show)
53
54
55instance ToJSON FoxReplace where
56 toJSON (FoxReplace groupSet) = object
57 [ "version" .= ("0.15" :: String)
58 , "groups" .= groupSet
59 ]
60
61instance ToJSON FoxReplaceGroup where
62 toJSON FoxReplaceGroup{..} = object
63 [ "name" .= groupName
64 , "html" .= groupHtmlMode
65 , "enabled" .= True
66 , "urls" .= groupUrls
67 , "substitutions" .= groupSubs
68 ]
69
70instance ToJSON FoxReplaceHTML where
71 toJSON NoHTML = String "none"
72 toJSON OutputOnlyHTML = String "output"
73 toJSON BothHTML = String "inputoutput"
74
75instance ToJSON FoxReplaceSub where
76 toJSON FoxReplaceSub{..} = object
77 [ "input" .= rInput
78 , "output" .= rOutput
79 , "inputType" .= rInputType
80 , "caseSensitive" .= rCaseSensitive
81 ]
82
83instance ToJSON SubInput where
84 toJSON TextInput = String "text"
85 toJSON WordInput = String "wholewords"
86 toJSON RegexpInput = String "regexp"
87
88
89main :: IO ()
90main = do
91 progName <- takeFileName <$> getProgName
92 case progName of
93 "mlmmj-exposed" -> do
94 args <- getArgs
95 case args of
96 [listDir, (map toLower -> extension)] -> do
97 setCurrentDirectory listDir
98 identities <- getIdentities
99 subscribers <- getSubscribers
100 let hashes = filter ((==) extension . snd) [((ident, sub), hash' (ident, sub)) | ident <- identities, sub <- subscribers]
101 case hashes of
102 [((_, recipient), _)] -> do
103 uuid <- UUID.nextRandom
104 let fName = "queue" </> "exposed" <.> uuidTrans uuid
105 uuidTrans = uuidTrans' . UUID.toString
106 where
107 uuidTrans' [] = []
108 uuidTrans' ('-':xs) = uuidTrans' xs
109 uuidTrans' (x:xs) = x : uuidTrans' xs
110 getContents >>= writeFile fName
111 hPrintf stdout "Forwarding mail to <%s>, subscribed to %s\n" recipient (takeBaseName listDir)
112 callProcess "@mlmmj@/bin/mlmmj-send" ["-L", listDir, "-l", "6", "-m", fName, "-T", recipient]
113 removeFile fName
114 [] -> die "Unknown extension"
115 _ -> die "Ambiguous extension"
116 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <recipientExtension>)") >> exitWith (ExitFailure 2)
117 "mlmmj-expose" -> do
118 args <- getArgs
119 case args of
120 [listDir, (map toLower -> ident)] -> do
121 setCurrentDirectory listDir
122 identities <- getIdentities
123 case ident `elem` identities of
124 True -> putStrLn "Identity is already known"
125 False -> writeFile "exposed.ids" . unlines $ ident : identities
126 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <senderIdentity>)") >> exitWith (ExitFailure 2)
127 "mlmmj-get-exposed" -> do
128 args <- getArgs
129 case args of
130 [(dropTrailingPathSeparator -> listDir), (map toLower -> ident)] -> do
131 setCurrentDirectory listDir
132 identities <- getIdentities
133 unless (ident `elem` identities) . die $ "Unknown sender: ‘" ++ ident ++ "’"
134 mapM_ (\sub -> putStrLn $ sub ++ " " ++ takeFileName listDir ++ "+" ++ hash' (ident, sub) ++ "@subs.lists.yggdrasil.li") =<< getSubscribers
135 (dropTrailingPathSeparator -> listDir) : (map toLower -> ident) : (map (map toLower) -> recipients) -> do
136 setCurrentDirectory listDir
137 identities <- getIdentities
138 unless (ident `elem` identities) . die $ "Unknown sender: ‘" ++ ident ++ "’"
139 subscribers <- getSubscribers
140 forM_ recipients $ \recipient -> do
141 unless (recipient `elem` subscribers) . die $ "Unknown recipient: ‘" ++ recipient ++ "’";
142 putStrLn $ takeFileName listDir ++ "+" ++ hash' (ident, recipient) ++ "@subs.lists.yggdrasil.li";
143 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <senderIdentity> [<recipient> [...]])") >> exitWith (ExitFailure 2)
144 "mlmmj-serve-exposed" -> do
145 args <- getArgs
146 case args of
147 [(dropTrailingPathSeparator -> listDir)] -> do
148 setCurrentDirectory listDir
149 subscribers <- getSubscribers
150 identities <- getIdentities
151
152 let
153 listName = takeBaseName listDir
154 replaceGroup ident = FoxReplaceGroup { groupName = ident ++ "." ++ listName
155 , groupHtmlMode = NoHTML
156 , groupUrls = Set.empty
157 , groupSubs = Set.fromList $ map (replaceSub ident) subscribers
158 }
159 replaceSub ident sub = FoxReplaceSub { rInput = listName ++ "\\+" ++ hash' (ident, sub) ++ "(@subs\\.lists\\.yggdrasil\\.li)?"
160 , rOutput = sub
161 , rInputType = RegexpInput
162 , rCaseSensitive = True
163 }
164
165 LBS.putStr . encodePretty . FoxReplace . Set.fromList $ map replaceGroup identities
166 putChar '\n'
167 _ -> hPutStrLn stderr "Called without expected arguments (<listDirectory>)" >> exitWith (ExitFailure 2)
168 _ -> hPutStrLn stderr ("Called under unsupported name ‘" ++ progName ++ "’") >> exitWith (ExitFailure 2)
169
170getIdentities :: IO [String]
171getIdentities = (filter (not . null) . lines <$> readFile "exposed.ids") `catchIOError` (\e -> if isDoesNotExistError e then return [] else ioError e)
172
173getSubscribers :: IO [String]
174getSubscribers = map (map toLower) . concat <$> mapM (flip catchIOError (\e -> if isDoesNotExistError e then return [] else ioError e) . readDir) ["subscribers.d", "digesters.d"]
175 where
176 readDir dir = concat <$> (mapM (fmap lines . readFile) . map (dir </>) . filter (not . (`elem` [".", ".."]))=<< (getDirectoryContents dir))
177
178hash' :: Show a => a -> String
179hash' = take len . map toLower . show . (hash :: CBS.ByteString -> Digest SHA256) . CBS.pack . map toLower . show
180
181len :: Int
182len = 32
diff --git a/ymir/mlmmj-expose.nix b/ymir/mlmmj-expose.nix
index 0873b0f7..b3f7499c 100644
--- a/ymir/mlmmj-expose.nix
+++ b/ymir/mlmmj-expose.nix
@@ -1,105 +1,27 @@
1{ config, pkgs, ... }: 1{ config, pkgs, ... }:
2 2
3let 3let
4 haskellEnv = pkgs.haskellPackages.ghcWithPackages (pkgs: with pkgs; [ filepath directory cryptonite bytestring uuid ]); 4 haskellEnv = pkgs.haskellPackages.ghcWithPackages dependencies;
5 dependencies = pkgs: with pkgs; [ filepath
6 directory
7 cryptonite
8 bytestring
9 uuid
10 aeson
11 aeson-pretty
12 ];
5 mlmmj-exposed = pkgs.stdenv.mkDerivation { 13 mlmmj-exposed = pkgs.stdenv.mkDerivation {
6 name = "mlmmj-exposed"; 14 name = "mlmmj-expose";
7 src = pkgs.writeText "mlmmj-exposed.hs" '' 15 src = pkgs.substituteAll {
8 {-# LANGUAGE ViewPatterns #-} 16 src = ./mlmmj-expose.hs;
9 17 inherit (pkgs) mlmmj;
10 import System.IO 18 };
11 import System.IO.Error
12 import System.FilePath
13 import System.Environment
14 import System.Exit
15 import System.Directory
16 import System.Process
17 import Text.Printf
18
19 import Data.Char
20
21 import Control.Monad
22
23 import Crypto.Hash
24
25 import qualified Data.ByteString.Lazy as LBS
26 import qualified Data.ByteString.Char8 as CBS
27
28 import qualified Data.UUID as UUID (toString)
29 import qualified Data.UUID.V4 as UUID (nextRandom)
30
31 main :: IO ()
32 main = do
33 progName <- takeFileName <$> getProgName
34 case progName of
35 "mlmmj-exposed" -> do
36 args <- getArgs
37 case args of
38 [listDir, (map toLower -> extension)] -> do
39 setCurrentDirectory listDir
40 identities <- getIdentities
41 subscribers <- getSubscribers
42 let hashes = filter ((==) extension . snd) [((ident, sub), hash' (ident, sub)) | ident <- identities, sub <- subscribers]
43 case hashes of
44 [((_, recipient), _)] -> do
45 uuid <- UUID.nextRandom
46 let fName = "queue" </> "exposed" <.> uuidTrans uuid
47 uuidTrans = uuidTrans' . UUID.toString
48 where
49 uuidTrans' [] = []
50 uuidTrans' ('-':xs) = uuidTrans' xs
51 uuidTrans' (x:xs) = x : uuidTrans' xs
52 getContents >>= writeFile fName
53 hPrintf stdout "Forwarding mail to <%s>, subscribed to %s\n" recipient (takeBaseName listDir)
54 callProcess "${pkgs.mlmmj}/bin/mlmmj-send" ["-L", listDir, "-l", "6", "-m", fName, "-T", recipient]
55 removeFile fName
56 [] -> die "Unknown extension"
57 _ -> die "Ambiguous extension"
58 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <recipientExtension>)") >> exitWith (ExitFailure 2)
59 "mlmmj-expose" -> do
60 args <- getArgs
61 case args of
62 [listDir, (map toLower -> ident)] -> do
63 setCurrentDirectory listDir
64 identities <- getIdentities
65 case ident `elem` identities of
66 True -> putStrLn "Identity is already known"
67 False -> writeFile "exposed.ids" . unlines $ ident : identities
68 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <senderIdentity>)") >> exitWith (ExitFailure 2)
69 "mlmmj-get-exposed" -> do
70 args <- getArgs
71 case args of
72 (dropTrailingPathSeparator -> listDir) : (map toLower -> ident) : (map (map toLower) -> recipients) -> do
73 setCurrentDirectory listDir
74 identities <- getIdentities
75 unless (ident `elem` identities) . die $ "Unknown sender: ‘" ++ ident ++ "’"
76 subscribers <- getSubscribers
77 forM_ recipients (\recipient -> do {
78 unless (recipient `elem` subscribers) . die $ "Unknown recipient: ‘" ++ recipient ++ "’";
79 putStrLn $ takeFileName listDir ++ "+" ++ hash' (ident, recipient) ++ "@subs.lists.yggdrasil.li";
80 })
81 _ -> hPutStrLn stderr ("Called without expected arguments (<listDirectory> <senderIdentity> [<recipient> [...]])") >> exitWith (ExitFailure 2)
82 _ -> hPutStrLn stderr ("Called under unsupported name ‘" ++ progName ++ "’") >> exitWith (ExitFailure 2)
83 getIdentities :: IO [String]
84 getIdentities = (filter (not . null) . lines <$> readFile "exposed.ids") `catchIOError` (\e -> if isDoesNotExistError e then return [] else ioError e)
85
86 getSubscribers :: IO [String]
87 getSubscribers = map (map toLower) . concat <$> mapM (flip catchIOError (\e -> if isDoesNotExistError e then return [] else ioError e) . readDir) ["subscribers.d", "digesters.d"]
88 where
89 readDir dir = concat <$> (mapM (fmap lines . readFile) . map (dir </>) . filter (not . (`elem` [".", ".."]))=<< (getDirectoryContents dir))
90
91 hash' :: Show a => a -> String
92 hash' = take len . map toLower . show . (hash :: CBS.ByteString -> Digest SHA256) . CBS.pack . map toLower . show
93
94 len :: Int
95 len = 32
96 '';
97 buildCommand = '' 19 buildCommand = ''
98 mkdir -p $out/bin 20 mkdir -p $out/bin
99 #cp $src $out/bin/.mlmmj-exposed 21 #cp $src $out/bin/.mlmmj-exposed
100 ${haskellEnv}/bin/ghc -o $out/bin/.mlmmj-exposed -odir . -hidir . $src 22 ${haskellEnv}/bin/ghc -o $out/bin/.mlmmj-expose -odir . -hidir . $src
101 for f in mlmmj-exposed mlmmj-expose mlmmj-get-exposed; do 23 for f in mlmmj-exposed mlmmj-expose mlmmj-get-exposed mlmmj-serve-exposed; do
102 ln -s .mlmmj-exposed $out/bin/$f 24 ln -s .mlmmj-expose $out/bin/$f
103 done 25 done
104 ''; 26 '';
105 }; 27 };
diff --git a/ymir/zones/email.nights.soa b/ymir/zones/email.nights.soa
new file mode 100644
index 00000000..ac31f254
--- /dev/null
+++ b/ymir/zones/email.nights.soa
@@ -0,0 +1,34 @@
1$ORIGIN nights.email.
2$TTL 3600
3@ IN SOA ns.yggdrasil.li. root.yggdrasil.li. (
4 2017012701 ; serial
5 10800 ; refresh
6 3600 ; retry
7 604800 ; expire
8 3600 ; min TTL
9)
10 IN NS ns.yggdrasil.li.
11 IN NS ns.inwx.de.
12 IN NS ns2.inwx.de.
13 IN NS ns3.inwx.eu.
14 IN NS ns4.inwx.com.
15 IN NS ns5.inwx.net.
16
17@ IN A 188.68.51.254
18@ IN AAAA 2a03:4000:6:d004::
19@ IN MX 0 ymir.yggdrasil.li.
20@ IN TXT "v=spf1 redirect=yggdrasil.li"
21
22* IN A 188.68.51.254
23* IN AAAA 2a03:4000:6:d004::
24* IN MX 0 ymir.yggdrasil.li.
25* IN TXT "v=spf1 redirect=yggdrasil.li"
26
27ymir._domainkey IN TXT (
28 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2"
29 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
30 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
31)
32
33_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
34_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.
diff --git a/ymir/zones/index.nix b/ymir/zones/index.nix
index 8424a0e0..05da73f1 100644
--- a/ymir/zones/index.nix
+++ b/ymir/zones/index.nix
@@ -5,8 +5,9 @@ with lib;
5rec { 5rec {
6 "141.li" = { data = readFile ./li.141.soa; }; 6 "141.li" = { data = readFile ./li.141.soa; };
7 "dirty-haskell.org" = { data = readFile ./org.dirty-haskell.soa; }; 7 "dirty-haskell.org" = { data = readFile ./org.dirty-haskell.soa; };
8 "lmu.li" = { data = readFile ./li.lmu.soa; };
9 "praseodym.org" = { data = readFile ./org.praseodym.soa; }; 8 "praseodym.org" = { data = readFile ./org.praseodym.soa; };
10 "xmpp.li" = { data = readFile ./li.xmpp.soa; }; 9 "xmpp.li" = { data = readFile ./li.xmpp.soa; };
11 "yggdrasil.li" = { data = readFile ./li.yggdrasil.soa; }; 10 "yggdrasil.li" = { data = readFile ./li.yggdrasil.soa; };
11 "kleen.li" = { data = readFile ./li.kleen.soa; };
12 "nights.email" = { data = readFile ./email.nights.soa; };
12} 13}
diff --git a/ymir/zones/li.lmu.soa b/ymir/zones/li.kleen.soa
index d1e05738..8c5af16e 100644
--- a/ymir/zones/li.lmu.soa
+++ b/ymir/zones/li.kleen.soa
@@ -1,7 +1,7 @@
1$ORIGIN lmu.li. 1$ORIGIN kleen.li.
2$TTL 3600 2$TTL 3600
3@ IN SOA ns.yggdrasil.li. root.yggdrasil.li. ( 3@ IN SOA ns.yggdrasil.li. root.yggdrasil.li. (
4 2016111011 ; serial 4 2017012601 ; serial
5 10800 ; refresh 5 10800 ; refresh
6 3600 ; retry 6 3600 ; retry
7 604800 ; expire 7 604800 ; expire
@@ -16,16 +16,19 @@ $TTL 3600
16 16
17@ IN A 188.68.51.254 17@ IN A 188.68.51.254
18@ IN AAAA 2a03:4000:6:d004:: 18@ IN AAAA 2a03:4000:6:d004::
19@ IN MX 10 ymir.yggdrasil.li. 19@ IN MX 0 ymir.yggdrasil.li.
20@ IN TXT "v=spf1 redirect=yggdrasil.li" 20@ IN TXT "v=spf1 redirect=yggdrasil.li"
21 21
22* IN A 188.68.51.254 22* IN A 188.68.51.254
23* IN AAAA 2a03:4000:6:d004:: 23* IN AAAA 2a03:4000:6:d004::
24* IN MX 0 ymir.yggdrasil.li. 24* IN MX 0 ymir.yggdrasil.li.
25* IN TXT "v=spf1 redirect=yggdrasil.li" 25* IN TXT "v=spf1 redirect=yggdrasil.li"
26 26
27ymir._domainkey IN TXT ( 27ymir._domainkey IN TXT (
28 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2" 28 "v=DKIM1;k=rsa;p=MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq3cCKlk+VPhyAanLZTM0BCzUT/+fmxHioZcFk0uJk1akBYj7BRofR7eVNcLKpm3rwYMQgE+9vJH9p8SV6tws9EcWc8SMCqqGZlREYM7PmLDiTSK/vjCzkygfgFCb0EBNsY2A/fpP4rTeoxrbcBSvMkq97iY5rwyw4wXZVZXLiDaCj23s8POoxTk1ClqUJZJQ5x2"
29 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24" 29 "qzrC0RfN5kLZ9A7Gq2jB09vNxpXHYqABA0bJv88JiZM7hfkp9IafJZ+yCVMaBcJs4DAxnTjNAuFD9gm+qSFVY8+yeXqL6Qjo5PbruhyZRBW8RgRYT8t5n07XRglMGKKGMwOGLanrltcyXqB+GsDZBD36RAAwjFadnxdpDyRv4SgRP7ff2tKRrORYpmpN+mKdqw5j3J/nP6bXV1oAkyh9XQkPEIDi81WT87EZziTElDzVp6A2qFOxqucAovoRk24"
30 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ==" 30 "7vlsns1FApFRsp9mja0UZNObyKD1M6tP9Ep7lS76tFGMk+WDvXRJH5LEsyCpu7sSyl1r/O0M4K+KldRCqLlZd7rf8F5P8T0dn1azk05g7F4p0N/y9GNdzXbPZ9u0eZdI7SEdh8ZoOZp7NVZiBFfbWLSS5ZtyA2kbBa4i7GJ/cuAbEKOmqAkeQPiu96TGIcyjkXjS6mTPI+9UmKZYZC+OM8XdJ02y5KRoonCc19ZS8CAwEAAQ=="
31) 31)
32
33_xmpp-client._tcp IN SRV 5 0 5222 ymir.yggdrasil.li.
34_xmpp-server._tcp IN SRV 5 0 5269 ymir.yggdrasil.li.