2 This module contains some functions that are useful in several places in the
3 program and don't belong to one specific other module.
5 module Gnutella.Misc where
7 import Data.ByteString(ByteString)
8 import qualified Data.ByteString as BS
12 import Data.Char(isNumber)
13 import Data.List(intersperse)
15 import Network.BSD(getHostByName, HostEntry(..))
16 import Network.Socket(HostAddress(..))
20 Maakt van vier bytes een Word32. Gaat ervan uit dat die vier bytes little-endian achter elkaar
21 staan. Als de gegeven string korter is dan 4 bytes, termineert de functie. Als de string langer
22 is, worden alle bytes voorbij de vierde genegeerd.
24 composeWord32 :: ByteString -> Word32
25 composeWord32 s = shiftL byte4 24 + shiftL byte3 16 + shiftL byte2 8 + byte1
26 where byte1, byte2, byte3, byte4 :: Word32
27 [byte1, byte2, byte3, byte4] = map fromIntegral $ BS.unpack (BS.take 4 s)
30 Turns a Word32 into a tuple of Word8s. The tuple is little-endian: the least
31 significant octet comes first.
33 word32ToWord8s :: Word32 -> (Word8, Word8, Word8, Word8)
34 word32ToWord8s w = (fromIntegral (w .&. 0x000000ff)
35 ,fromIntegral (shiftR w 8 .&. 0x000000ff)
36 ,fromIntegral (shiftR w 16 .&. 0x000000ff)
37 ,fromIntegral (shiftR w 24 .&. 0x000000ff)
41 Parses a host specification in the "name:12345"-style notation into a hostname
44 As a rather special feature, it returns 6346 as the port number when there is
45 no port specified. When there is a port specified, but it is unparseable, it
48 parseHostnameWithPort :: String -> IO (Maybe ((Word8, Word8, Word8, Word8)
50 parseHostnameWithPort str = do maybeHostName <- stringToIP hostNameStr
51 return $ (do portNum <- maybePortNum
52 hostName <- maybeHostName
53 return (hostName, portNum)
55 where hostNameStr = takeWhile (/=':') str
56 maybePortNum = case tail (dropWhile (/=':') str) of
60 (x:xs) -> Just $ fromIntegral $ fst x
63 Translates a string, representing an IP address, to a list of bytes.
64 Returns Nothing when the string does not represent an IP address in xxx.xxx.xxx.xxx format
66 ipStringToBytes :: String -> Maybe (Word8, Word8, Word8, Word8)
67 -- Again, hugs won't let us use regexes where they would be damn convenient
69 let ipBytesStrings = splitAtDots s
70 in if all (all isNumber) ipBytesStrings
71 then let bytesList = map (fst . head . reads) ipBytesStrings
78 where splitAtDots s = foldr (\c (n:nums) -> if c == '.'
84 Translates a list of bytes representing an IP address (big endian) to a string
85 in the xxx.xxx.xxx.xxx format.
87 ipBytesToString :: (Word8, Word8, Word8, Word8) -> String
88 ipBytesToString (b1, b2, b3, b4) =
89 concat $ intersperse "." $ map show [b1, b2, b3, b4]
92 Takes a String that's either an IP address or a hostname, and returns you the
93 IP address as a list of 4 bytes (in big-endian byte order). It returns Nothing
94 if there is no parse for the string as IP address and the hostname can't be
97 stringToIP :: String -> IO (Maybe (Word8, Word8, Word8, Word8))
98 stringToIP hostName = case ipStringToBytes hostName of
99 Just a -> return (Just a)
100 Nothing -> do hostent <- getHostByName hostName
101 let ipWord32 = head (hostAddresses hostent)
102 ipWord8s = word32ToWord8s ipWord32
103 return (Just ipWord8s)
105 -- used in reading the hostcache
106 instance Read PortNumber where
107 readsPrec i = map (\(a, b) -> (fromIntegral a, b)) . (readsPrec i :: ReadS Word16)