| | 1 | {-| |
|---|
| | 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. |
|---|
| | 4 | -} |
|---|
| | 5 | module Gnutella.Misc where |
|---|
| | 6 | |
|---|
| | 7 | import Data.ByteString(ByteString) |
|---|
| | 8 | import qualified Data.ByteString as BS |
|---|
| | 9 | import Data.Bits |
|---|
| | 10 | import Data.Word |
|---|
| | 11 | import Text.Read |
|---|
| | 12 | import Data.Char(isNumber) |
|---|
| | 13 | import Data.List(intersperse) |
|---|
| | 14 | import Network |
|---|
| | 15 | import Network.BSD(getHostByName, HostEntry(..)) |
|---|
| | 16 | import Network.Socket(HostAddress(..)) |
|---|
| | 17 | import Debug.Trace |
|---|
| | 18 | |
|---|
| | 19 | {-| |
|---|
| | 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. |
|---|
| | 23 | -} |
|---|
| | 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) |
|---|
| | 28 | |
|---|
| | 29 | {-| |
|---|
| | 30 | Turns a Word32 into a tuple of Word8s. The tuple is little-endian: the least |
|---|
| | 31 | significant octet comes first. |
|---|
| | 32 | -} |
|---|
| | 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) |
|---|
| | 38 | ) |
|---|
| | 39 | |
|---|
| | 40 | {-| |
|---|
| | 41 | Parses a host specification in the "name:12345"-style notation into a hostname |
|---|
| | 42 | and a port number. |
|---|
| | 43 | |
|---|
| | 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 |
|---|
| | 46 | returns Nothing. |
|---|
| | 47 | -} |
|---|
| | 48 | parseHostnameWithPort :: String -> IO (Maybe ((Word8, Word8, Word8, Word8) |
|---|
| | 49 | ,PortNumber)) |
|---|
| | 50 | parseHostnameWithPort str = do maybeHostName <- stringToIP hostNameStr |
|---|
| | 51 | return $ (do portNum <- maybePortNum |
|---|
| | 52 | hostName <- maybeHostName |
|---|
| | 53 | return (hostName, portNum) |
|---|
| | 54 | ) |
|---|
| | 55 | where hostNameStr = takeWhile (/=':') str |
|---|
| | 56 | maybePortNum = case tail (dropWhile (/=':') str) of |
|---|
| | 57 | [] -> Just $ 6346 |
|---|
| | 58 | s -> case reads s of |
|---|
| | 59 | [] -> Nothing |
|---|
| | 60 | (x:xs) -> Just $ fromIntegral $ fst x |
|---|
| | 61 | |
|---|
| | 62 | {-| |
|---|
| | 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 |
|---|
| | 65 | -} |
|---|
| | 66 | ipStringToBytes :: String -> Maybe (Word8, Word8, Word8, Word8) |
|---|
| | 67 | -- Again, hugs won't let us use regexes where they would be damn convenient |
|---|
| | 68 | ipStringToBytes s = |
|---|
| | 69 | let ipBytesStrings = splitAtDots s |
|---|
| | 70 | in if all (all isNumber) ipBytesStrings |
|---|
| | 71 | then let bytesList = map (fst . head . reads) ipBytesStrings |
|---|
| | 72 | in Just (bytesList!!0 |
|---|
| | 73 | ,bytesList!!1 |
|---|
| | 74 | ,bytesList!!2 |
|---|
| | 75 | ,bytesList!!3 |
|---|
| | 76 | ) |
|---|
| | 77 | else Nothing |
|---|
| | 78 | where splitAtDots s = foldr (\c (n:nums) -> if c == '.' |
|---|
| | 79 | then [] : n : nums |
|---|
| | 80 | else (c:n) : nums |
|---|
| | 81 | ) [[]] s |
|---|
| | 82 | |
|---|
| | 83 | {-| |
|---|
| | 84 | Translates a list of bytes representing an IP address (big endian) to a string |
|---|
| | 85 | in the xxx.xxx.xxx.xxx format. |
|---|
| | 86 | -} |
|---|
| | 87 | ipBytesToString :: (Word8, Word8, Word8, Word8) -> String |
|---|
| | 88 | ipBytesToString (b1, b2, b3, b4) = |
|---|
| | 89 | concat $ intersperse "." $ map show [b1, b2, b3, b4] |
|---|
| | 90 | |
|---|
| | 91 | {-| |
|---|
| | 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 |
|---|
| | 95 | found. |
|---|
| | 96 | -} |
|---|
| | 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) |
|---|
| | 104 | |
|---|
| | 105 | -- used in reading the hostcache |
|---|
| | 106 | instance Read PortNumber where |
|---|
| | 107 | readsPrec i = map (\(a, b) -> (fromIntegral a, b)) . (readsPrec i :: ReadS Word16) |
|---|
| | 108 | |