File: haskell1.hs

package info (click to toggle)
ohcount 3.0.0-2
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 4,712 kB
  • ctags: 3,205
  • sloc: ansic: 6,524; ruby: 2,560; perl: 2,041; erlang: 350; lisp: 272; sh: 244; pascal: 196; vhdl: 150; haskell: 149; asm: 128; cs: 124; awk: 98; java: 92; php: 73; tcl: 58; xml: 57; fortran: 54; makefile: 32; python: 31; ada: 30; objc: 30; jsp: 28; sql: 18; cobol: 13; ml: 9; cpp: 3
file content (108 lines) | stat: -rw-r--r-- 4,521 bytes parent folder | download | duplicates (8)
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
101
102
103
104
105
106
107
108
{-|
  This module contains some functions that are useful in several places in the
  program and don't belong to one specific other module.
-}
module Gnutella.Misc where

import Data.ByteString(ByteString)
import qualified Data.ByteString as BS
import Data.Bits
import Data.Word
import Text.Read
import Data.Char(isNumber)
import Data.List(intersperse)
import Network
import Network.BSD(getHostByName, HostEntry(..))
import Network.Socket(HostAddress(..))
import Debug.Trace

{-|
  Maakt van vier bytes een Word32. Gaat ervan uit dat die vier bytes little-endian achter elkaar
  staan. Als de gegeven string korter is dan 4 bytes, termineert de functie. Als de string langer
  is, worden alle bytes voorbij de vierde genegeerd.
-}
composeWord32 :: ByteString -> Word32
composeWord32 s = shiftL byte4 24 + shiftL byte3 16 + shiftL byte2 8 + byte1
  where byte1, byte2, byte3, byte4 :: Word32
        [byte1, byte2, byte3, byte4] = map fromIntegral $ BS.unpack (BS.take 4 s)

{-| 
  Turns a Word32 into a tuple of Word8s. The tuple is little-endian: the least
  significant octet comes first.
-}
word32ToWord8s :: Word32 -> (Word8, Word8, Word8, Word8)
word32ToWord8s w = (fromIntegral (w .&. 0x000000ff)
                   ,fromIntegral (shiftR w 8 .&. 0x000000ff)
                   ,fromIntegral (shiftR w 16 .&. 0x000000ff)
                   ,fromIntegral (shiftR w 24 .&. 0x000000ff)
                   )

{-|
  Parses a host specification in the "name:12345"-style notation into a hostname
  and a port number.

  As a rather special feature, it returns 6346 as the port number when there is
  no port specified. When there is a port specified, but it is unparseable, it
  returns Nothing.
-}
parseHostnameWithPort :: String -> IO (Maybe ((Word8, Word8, Word8, Word8)
                                             ,PortNumber))
parseHostnameWithPort str = do maybeHostName <- stringToIP hostNameStr
                               return $ (do portNum <- maybePortNum
                                            hostName <- maybeHostName
                                            return (hostName, portNum)
                                        )
  where hostNameStr = takeWhile (/=':') str
        maybePortNum  = case tail (dropWhile (/=':') str) of
                          [] -> Just $ 6346
                          s  -> case reads s of
                                  []     -> Nothing
                                  (x:xs) -> Just $ fromIntegral $ fst x

{-|
  Translates a string, representing an IP address, to a list of bytes.
  Returns Nothing when the string does not represent an IP address in xxx.xxx.xxx.xxx format
-}
ipStringToBytes :: String -> Maybe (Word8, Word8, Word8, Word8)
-- Again, hugs won't let us use regexes where they would be damn convenient
ipStringToBytes s =
    let ipBytesStrings = splitAtDots s
    in if all (all isNumber) ipBytesStrings
         then let bytesList = map (fst . head . reads) ipBytesStrings
              in Just (bytesList!!0
                      ,bytesList!!1
                      ,bytesList!!2
                      ,bytesList!!3
                      )
         else Nothing
  where splitAtDots s = foldr (\c (n:nums) -> if c == '.'
                                              then [] : n : nums
                                              else (c:n) : nums
                              ) [[]] s

{-|
  Translates a list of bytes representing an IP address (big endian) to a string
  in the xxx.xxx.xxx.xxx format.
-}
ipBytesToString :: (Word8, Word8, Word8, Word8) -> String
ipBytesToString (b1, b2, b3, b4) = 
    concat $ intersperse "." $ map show [b1, b2, b3, b4]

{-| 
  Takes a String that's either an IP address or a hostname, and returns you the
  IP address as a list of 4 bytes (in big-endian byte order). It returns Nothing
  if there is no parse for the string as IP address and the hostname can't be
  found.
-}
stringToIP :: String -> IO (Maybe (Word8, Word8, Word8, Word8))
stringToIP hostName = case ipStringToBytes hostName of
                        Just a  -> return (Just a)
                        Nothing -> do hostent <- getHostByName hostName
                                      let ipWord32 = head (hostAddresses hostent)
                                          ipWord8s = word32ToWord8s ipWord32
                                      return (Just ipWord8s)

-- used in reading the hostcache
instance Read PortNumber where
    readsPrec i = map (\(a, b) -> (fromIntegral a, b)) . (readsPrec i :: ReadS Word16)