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]