summaryrefslogtreecommitdiff
path: root/dht/examples/readnodes.hs
blob: 3ecffbafac6ad2244f76a823d99f40580282d15b (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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import Control.Monad
import qualified Data.ByteArray as BA
import Data.ByteString (hPutStr,pack)
import Data.Char
import Data.Function
import Data.Int
import Data.List
import Data.Maybe
import Data.Word
import Foreign.Ptr
import Foreign.Marshal.Utils
import Foreign.Storable
import System.Environment
import System.IO (stdout)
import Text.Read

import qualified Network.Tox.NodeId as UDP
import Network.Tox.TCP.NodeId as TCP

-- struct bootstrap_node {
-- 8   char *address;
-- 2   bool ipv6;
-- 2   uint16_t port_udp;
-- 2   uint16_t port_tcp;
-- 32  uint8_t key[32];
-- 2
-- } bootstrap_nodes[] = {


int32_bytes :: Int32 -> IO [Word8]
int32_bytes = int_bytes

int16_bytes :: Int16 -> IO [Word8]
int16_bytes i = int_bytes i

int_bytes :: Storable a => a -> IO [Word8]
int_bytes i = with i $ \p0 ->
    let p = castPtr p0
    in foldr (\g r -> g >>= \x -> fmap (x :) r) (return [])
        $ peekElemOff p `map` [ 0 .. sizeOf i - 1 ]


node_bytes :: Int32 -> Int -> String -> NodeInfo -> IO [Word8]
node_bytes num_nodes a s n = do
    -- amd64 8 + 2 + 2 + 2 + 32 + 2 = 48
    --       sizeof(bootstrap_node) = 48
    -- i386 4 + 2 + 2 + 2 + 32 + 2 = 44
    --      sizeof(bootstrap_node) = 44
    bigendian <- (==0) . head <$> int32_bytes 1
    let sz = sizeOf (0::Int) + 2 + 2 + 2 + 32 + 2
        base = fromIntegral num_nodes * sz
        ip6 = case find (==':') s of
            Just _ | bigendian -> [0,1]
                   | otherwise -> [1,0]
            _ -> [0,0]
        nid = UDP.nodeId $ udpNodeInfo n
    adr <- int_bytes (base + a)
    u <- int16_bytes $ fromIntegral $ UDP.nodePort $ udpNodeInfo n
    t <- int16_bytes $ fromIntegral $ tcpPort n
    return $ foldr (++) []
        [ adr                        -- char *address;
        , ip6                        -- bool ipv6;
        , u                          -- uint16_t port_udp
        , t                          -- uint16_t port_tcp
        , BA.unpack (UDP.id2key nid) -- uint8_t key[32];
        , [0,0]                      -- padding
        ]

addressString :: NodeInfo -> String
addressString ni = case show (UDP.nodeAddr $ udpNodeInfo ni) of
    '[':xs -> takeWhile (/=']') xs
    xs     -> takeWhile (/=':') xs

readNode :: String -> Maybe NodeInfo
readNode nstr = mplus (readMaybe nstr)
                      (fromUDPNode <$> readMaybe nstr)

main = do
    args <- getArgs
    let [ifilename] = args
    ws <- words <$> readFile ifilename
    let ns :: [ NodeInfo ]
        ns = mapMaybe readNode ws
        num_nodes :: Int32
        num_nodes = fromIntegral $ length ns
        ptr_size :: Int32
        ptr_size = fromIntegral $ sizeOf (0 :: Int)
        ss = map addressString ns
        as = scanl (\i a -> length a + i + 1) 0 ss
    let h = stdout
    nbs <- int32_bytes num_nodes
    pbs <- int32_bytes ptr_size
    hPutStr h $ pack $ nbs ++ pbs
    forM_ (zip3 as ss ns) $ \(a,s,n) -> do
        bs <- node_bytes num_nodes a s n
        hPutStr h $ pack bs
    hPutStr h $ pack $ do
        adr <- ss
        map (fromIntegral . ord) adr ++ [0]