Browse Source

!!!TlsSettings changed!!!
Added some tests, and fixed some bugs they found.

Marcos Dumay de Medeiros 8 years ago
parent
commit
ff97fd0f01
12 changed files with 326 additions and 74 deletions
  1. 16 13
      src/System/IO/Uniform/Targets.hs
  2. 48 16
      src/System/IO/Uniform/ds.c
  3. 1 1
      src/System/IO/Uniform/ds.h
  4. 16 0
      test/Base.hs
  5. 88 0
      test/Blocking.hs
  6. 0 42
      test/Lazyness.hs
  7. 82 0
      test/Targets.hs
  8. 22 0
      test/cert.pem
  9. 8 0
      test/dh.pem
  10. 28 0
      test/key.pem
  11. 1 0
      test/testFile
  12. 16 2
      uniform-io.cabal

+ 16 - 13
src/System/IO/Uniform/Targets.hs

@@ -2,6 +2,7 @@
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE ForeignFunctionInterface #-}
 {-# LANGUAGE InterruptibleFFI #-}
+{-# LANGUAGE EmptyDataDecls #-}
 
 module System.IO.Uniform.Targets (TlsSettings(..), UniformIO(..), SocketIO, FileIO, TlsStream, BoundedPort, SomeIO(..), connectTo, connectToHost, bindPort, accept, openFile, getPeer, closePort) where
 
@@ -24,10 +25,10 @@ import GHC.Conc (closeFdWith, threadWaitRead, threadWaitWrite)
 import System.Posix.Types (Fd(..))
 
 -- | Settings for starttls functions.
-data TlsSettings = TlsSettings {tlsPrivateKeyFile :: String, tlsCertificateChainFile :: String} deriving (Read, Show)
+data TlsSettings = TlsSettings {tlsPrivateKeyFile :: String, tlsCertificateChainFile :: String, tlsDHParametersFile :: String} deriving (Read, Show)
 
 instance Default TlsSettings where
-  def = TlsSettings "" ""
+  def = TlsSettings "" "" ""
 
 -- |
 -- Typeclass for uniform IO targets.
@@ -96,11 +97,13 @@ instance UniformIO SocketIO where
     closeFdWith closeFd f
   startTls st s = withCString (tlsCertificateChainFile st) (
     \cert -> withCString (tlsPrivateKeyFile st) (
-      \key -> do
-        r <- c_startSockTls (sock s) cert key
-        if r == nullPtr
-          then throwErrno "could not start TLS"
-          else return . TlsStream $ r
+      \key -> withCString (tlsDHParametersFile st) (
+        \para -> do
+          r <- c_startSockTls (sock s) cert key para
+          if r == nullPtr
+            then throwErrno "could not start TLS"
+            else return . TlsStream $ r
+        )
       )
     )
   isSecure _ = False
@@ -259,13 +262,13 @@ closeFd (Fd f) = c_closeFd f
 closePort :: BoundedPort -> IO ()
 closePort p = c_closePort (lis p)
 
-foreign import ccall safe "getPort" c_getPort :: CInt -> IO (Ptr Nethandler)
-foreign import ccall safe "createFromHandler" c_accept :: Ptr Nethandler -> IO (Ptr Ds)
+foreign import ccall interruptible "getPort" c_getPort :: CInt -> IO (Ptr Nethandler)
+foreign import ccall interruptible "createFromHandler" c_accept :: Ptr Nethandler -> IO (Ptr Ds)
 foreign import ccall safe "createFromFileName" c_createFile :: CString -> IO (Ptr Ds)
-foreign import ccall safe "createToIPv4Host" c_connect4 :: CUInt -> CInt -> IO (Ptr Ds)
-foreign import ccall safe "createToIPv6Host" c_connect6 :: Ptr CUChar -> CInt -> IO (Ptr Ds)
+foreign import ccall interruptible "createToIPv4Host" c_connect4 :: CUInt -> CInt -> IO (Ptr Ds)
+foreign import ccall interruptible "createToIPv6Host" c_connect6 :: Ptr CUChar -> CInt -> IO (Ptr Ds)
 
-foreign import ccall safe "startSockTls" c_startSockTls :: Ptr Ds -> CString -> CString -> IO (Ptr TlsDs)
+foreign import ccall interruptible "startSockTls" c_startSockTls :: Ptr Ds -> CString -> CString -> CString -> IO (Ptr TlsDs)
 foreign import ccall safe "getPeer" c_getPeer :: Ptr Ds -> Ptr CUInt -> Ptr CUChar -> Ptr CInt -> IO (CInt)
 
 foreign import ccall safe "getFd" c_getFd :: Ptr Ds -> CInt
@@ -273,7 +276,7 @@ foreign import ccall safe "closeFd" c_closeFd :: CInt -> IO ()
 
 foreign import ccall safe "prepareToClose" c_prepareToClose :: Ptr Ds -> IO CInt
 foreign import ccall safe "closeHandler" c_closePort :: Ptr Nethandler -> IO ()
-foreign import ccall safe "closeTlsDs" c_closeTls :: Ptr TlsDs -> IO (Ptr Ds)
+foreign import ccall safe "closeTls" c_closeTls :: Ptr TlsDs -> IO (Ptr Ds)
 
 foreign import ccall interruptible "sendDs" c_send :: Ptr Ds -> Ptr CChar -> CInt -> IO CInt
 foreign import ccall interruptible "stdDsSend" c_sendStd :: Ptr CChar -> CInt -> IO CInt

+ 48 - 16
src/System/IO/Uniform/ds.c

@@ -25,14 +25,14 @@ void *clear(void *ptr){
   return NULL;
 }
 
-void loadOpenSSL(){
+void loadOpenSSL(const char *dh){
   if(!openSslLoaded){
-    openSslLoaded = 1;
     SSL_load_error_strings();
     ERR_load_BIO_strings();
     ERR_load_crypto_strings();
     SSL_library_init();
-    OpenSSL_add_all_algorithms();
+    OpenSSL_add_all_algorithms();    
+    openSslLoaded = 1;    
   }
 }
 
@@ -96,7 +96,7 @@ ds createFromFile(int f){
 }
 
 ds createFromFileName(const char *f){
-  int fd = open(f, O_CREAT | O_RDWR);
+  int fd = open(f, O_CREAT | O_RDWR, 0666);
   if(fd == -1){
     return NULL;
   }
@@ -201,10 +201,11 @@ int prepareToClose(ds d){
   return fd;
 }
 
-ds closeTlsDs(tlsDs d){
+ds closeTls(tlsDs d){
   ds original = d->original;
   SSL_shutdown(d->s);
-  SSL_shutdown(d->s);
+  //No bidirectional shutdown supported
+  //SSL_shutdown(d->s);
   SSL_free(d->s);
   free(d);
   return original;
@@ -215,8 +216,10 @@ void closeHandler(nethandler h){
   free(h);
 }
 
-tlsDs startSockTls(ds d, const char *cert, const char *key){
-  loadOpenSSL();
+tlsDs startSockTls(ds d, const char *cert, const char *key, const char *dh){
+  fprintf(stderr, "Starting TLS\n");
+  loadOpenSSL(dh);
+  fprintf(stderr, "OpenSSL loaded\n");
   SSL_CTX * ctx = NULL;
   if(d->server)
     ctx = SSL_CTX_new(TLSv1_server_method());
@@ -224,19 +227,39 @@ tlsDs startSockTls(ds d, const char *cert, const char *key){
     ctx = SSL_CTX_new(TLSv1_client_method());
   if(!ctx)
     return NULL;
+  fprintf(stderr, "Got CTX\n");
+  if(d->server){
+    FILE *dhfile = fopen(dh, "r");
+    fprintf(stderr, "dh is %s\n", dh);
+    fprintf(stderr, "dhfile is %x\n", dhfile);
+    DH *dhdt = PEM_read_DHparams(dhfile, NULL, NULL, NULL);
+    fprintf(stderr, "dhdt is %x\n", dhdt);
+    fclose(dhfile);
+    if(SSL_CTX_set_tmp_dh(ctx, dhdt) <= 0){
+      int f = prepareToClose(d);
+      closeFd(f);
+      clear(dhdt);
+      return clear(ctx);    
+    }
+    fprintf(stderr, "Set DH parameters\n");
+  }
   SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+  fprintf(stderr, "Set CTX options\n");
+  fprintf(stderr, "Set options\n");
   if(cert)
     if(SSL_CTX_use_certificate_chain_file(ctx, cert) != 1){
       int f = prepareToClose(d);
       closeFd(f);
       return clear(ctx);
     }
+  fprintf(stderr, "Set cert\n");
   if(key)
     if(SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != 1){
       int f = prepareToClose(d);
       closeFd(f);
       return clear(ctx);
     }
+  fprintf(stderr, "Set key\n");
   tlsDs t = (tlsDs)malloc(sizeof(s_tlsDs));
   t->original = d;
   if(!(t->s = SSL_new(ctx))){
@@ -245,30 +268,39 @@ tlsDs startSockTls(ds d, const char *cert, const char *key){
     clear(ctx);
     return clear(t);
   }
+  fprintf(stderr, "Got SSL\n");
   if(!SSL_set_fd(t->s, d->fd)){
-    closeTlsDs(t);
+    closeTls(t);
     return NULL;
   }
+  fprintf(stderr, "Set fd\n");
   int retry = 1;
   int e;
   while(retry){
     retry = 0;
-    if(d->server)
+    if(d->server){
+      SSL_set_accept_state(t->s);
       e = SSL_accept(t->s);
-    else
+    }else{
+      SSL_set_connect_state(t->s);
       e = SSL_connect(t->s);
+    }
     if(e <= 0){
-      retry = 1;
-      int erval = SSL_get_error(t->s, e);
+      unsigned long erval = SSL_get_error(t->s, e);
+      char ertxt[300];
+      ERR_error_string(erval, ertxt);
+      fprintf(stderr, "SSL Error: %s\n", ertxt);
+      ERR_print_errors(t->s->bbio);
       if((erval == SSL_ERROR_WANT_READ) || (erval == SSL_ERROR_WANT_WRITE)){
-	
+	//Here goes support to non-blocking IO, once it's supported
+	//retry = 1;
       }else{
-	//ERR_print_errors(t->s->bbio);
-	closeTlsDs(t);
+	closeTls(t);
 	return NULL;
       }
     }
   }
+  fprintf(stderr, "TLS started\n");
   return t;
 }
 

+ 1 - 1
src/System/IO/Uniform/ds.h

@@ -36,7 +36,7 @@ ds createFromHandler(nethandler);
 ds createToIPv4Host(const unsigned long, const int);
 ds createToIPv6Host(const unsigned char[16], const int);
 
-tlsDs startSockTls(ds, const char*, const char*);
+tlsDs startSockTls(ds, const char*, const char*, const char*);
 
 int getPeer(ds, unsigned long*, unsigned char[16], int*);
 

+ 16 - 0
test/Base.hs

@@ -0,0 +1,16 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Base (simpleTest) where
+
+import Distribution.TestSuite
+
+simpleTest :: String -> IO Progress -> Test
+simpleTest n t = 
+  let test = TestInstance
+        {run = t,
+         name = n,
+         tags = [],
+         options = [],
+         setOption = \_ _ -> Right test
+        }
+  in Test test

+ 88 - 0
test/Blocking.hs

@@ -0,0 +1,88 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Blocking (tests) where
+
+import Distribution.TestSuite
+import Base (simpleTest)
+import Control.Concurrent(forkIO) 
+import qualified System.IO.Uniform as U
+import qualified System.IO.Uniform.Streamline as S
+import System.Timeout (timeout)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as C8
+import qualified Data.Attoparsec.ByteString as A
+--import Control.Monad.IO.Class (liftIO)
+
+tests :: IO [Test]
+tests = return [
+  simpleTest "recieveLine"
+  (successTimeout "A test\n" (restoreLine S.receiveLine)),
+  simpleTest "runAttoparsec with successful parser"
+  (successTimeout "abcde" (parseBS (A.string "abcde"))),
+  simpleTest "runAttoparsec with failed parser"
+  (failTimeout "abcde" (parseBS (A.string "c"))),
+  simpleTest "lazyRecieveLine"
+  (successTimeout "Another test\n" (concatLine S.lazyRecieveLine)),
+  simpleTest "lazyReceiveN"
+  (failTimeout "abcde" (concatLine (S.lazyReceiveN 5)))
+  ]
+
+parseBS :: A.Parser ByteString -> S.Streamline IO ByteString
+parseBS p = do
+  t <- S.runAttoparsec p
+  case t of
+    Left e -> return . C8.pack $ e
+    Right s -> return s
+
+restoreLine :: S.Streamline IO ByteString -> S.Streamline IO ByteString
+restoreLine f = do
+  l <- f
+  return $ BS.concat [l, "\n"]
+  
+concatLine :: S.Streamline IO [ByteString] -> S.Streamline IO ByteString
+concatLine f = do
+  l <- f
+  return . BS.concat $ l
+
+-- | Tests the given command, by sending a string to an echo and running the command.
+--   the command must not block.
+successTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
+successTimeout txt f = do
+  recv <- U.bindPort 8888
+  forkIO $ S.withClient (\_ _ -> do
+                            l <- f
+                            S.send l
+                            return ()
+                        ) recv
+  r' <- timeout 1000000 $ S.withServer (do
+                                     S.send txt
+                                     t <- f
+                                     if t == txt
+                                       then return . Finished $ Pass
+                                       else return . Finished . Fail . C8.unpack $ t
+                                 ) "127.0.0.1" 8888
+  U.closePort recv
+  case r' of
+    Just r -> return r
+    Nothing -> return . Finished . Fail $ "Execution blocked"
+
+-- | Tests the given command, by sending text trough the network and running it.
+--   Does not care about the result of the command, just wether it blocks.
+failTimeout :: ByteString -> S.Streamline IO ByteString -> IO Progress
+failTimeout txt f = do
+  recv <- U.bindPort 8888
+  forkIO $ S.withClient (\_ _ -> do
+                            f
+                            S.send "\n"
+                            return ()
+                        ) recv
+  r' <- timeout 1000000 $ S.withServer (do
+                                     S.send txt
+                                     S.receiveLine
+                                     return . Finished $ Pass
+                                 ) "127.0.0.1" 8888
+  U.closePort recv
+  case r' of
+    Just r -> return r
+    Nothing -> return . Finished . Fail $ "Execution blocked"

+ 0 - 42
test/Lazyness.hs

@@ -1,42 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Lazyness (tests) where
-
-import Distribution.TestSuite
-import Control.Concurrent(forkIO) 
-import qualified System.IO.Uniform as U
-import qualified System.IO.Uniform.Streamline as S
-import System.Timeout (timeout)  
-
-tests :: IO [Test]
-tests = return [Test readLine]
-  where
-    readLine = TestInstance
-      {run = testReadLine,
-       name = "Lazyness of readLine",
-       tags = [],
-       options = [],
-       setOption = \_ _ -> Right readLine
-      }
-      
-testReadLine :: IO Progress
-testReadLine = do
-  recv <- U.bindPort 8888
-  forkIO $ S.withClient (\_ _ -> do
-                            l <- S.receiveLine
-                            S.send l
-                            S.send "\n"
-                            return ()
-                        ) recv
-  r <- timeout 1000000 $ S.withServer (do
-                                     S.send "A test\n"
-                                     S.receiveLine
-                                     return ()
-                                 ) "127.0.0.1" 8888
-  case r of
-    Just _ -> return . Finished $ Pass
-    Nothing -> return . Finished . Fail $ "Timeout on Streamline.readLine"
-
-
-
-

+ 82 - 0
test/Targets.hs

@@ -0,0 +1,82 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Targets (tests) where
+
+import Distribution.TestSuite
+import Base (simpleTest)
+import Control.Concurrent(forkIO) 
+import qualified System.IO.Uniform as U
+import System.Timeout (timeout)
+import qualified Data.ByteString.Char8 as C8
+
+tests :: IO [Test]
+tests = return [
+  simpleTest "network" testNetwork,
+  simpleTest "file" testFile
+  --Test framework fails on this test
+  --actual script works as expected
+  --simpleTest "network TLS" testTls
+  ]
+
+testNetwork :: IO Progress
+testNetwork = do
+  recv <- U.bindPort 8888
+  forkIO $ do
+    s <- U.accept recv
+    l <- U.uRead s 100
+    U.uPut s l
+    U.uClose s
+    return ()
+  r' <- timeout 1000000 $ do
+        s <- U.connectToHost "127.0.0.1" 8888
+        let l = "abcdef\n"
+        U.uPut s l
+        l' <- U.uRead s 100
+        U.uClose s
+        if l == l'
+          then return . Finished $ Pass
+          else return . Finished . Fail . C8.unpack $ l'
+  U.closePort recv
+  case r' of
+    Just r -> return r
+    Nothing -> return . Finished . Fail $ "Execution blocked"
+
+testFile :: IO Progress
+testFile = do
+  let file = "test/testFile"
+  s <- U.openFile file
+  let l = "abcde\n"
+  U.uPut s l
+  U.uClose s
+  s' <- U.openFile file
+  l' <- U.uRead s' 100
+  U.uClose s'
+  if l == l'
+    then return . Finished $ Pass
+    else return . Finished . Fail . C8.unpack $ l'
+
+testTls :: IO Progress
+testTls = do
+  recv <- U.bindPort 8888
+  let set = U.TlsSettings "test/key.pem" "test/cert.pem" "test/dh.pem"
+  forkIO $ do
+    s' <- U.accept recv
+    s <- U.startTls set s'
+    l <- U.uRead s 100
+    U.uPut s l
+    U.uClose s
+    return ()
+  r' <- timeout 1000000 $ do
+    s' <- U.connectToHost "127.0.0.1" 8888
+    s <- U.startTls set s'
+    let l = "abcdef\n"
+    U.uPut s l
+    l' <- U.uRead s 100
+    U.uClose s
+    if l == l'
+      then return . Finished $ Pass
+      else return . Finished . Fail . C8.unpack $ l'
+  U.closePort recv
+  case r' of
+    Just r -> return r
+    Nothing -> return . Finished . Fail $ "Execution blocked"

+ 22 - 0
test/cert.pem

@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDoTCCAomgAwIBAgIJAM0NsBud2MA8MA0GCSqGSIb3DQEBCwUAMGcxCzAJBgNV
+BAYTAkJSMREwDwYDVQQIDAhCcmFzaWxpYTERMA8GA1UEBwwIQnJhc2lsaWExDjAM
+BgNVBAoMBVRlc3RlMQ4wDAYDVQQLDAVUZXN0ZTESMBAGA1UEAwwJbG9jYWxob3N0
+MB4XDTE1MDYyODAwNTQzNFoXDTE2MDYyNzAwNTQzNFowZzELMAkGA1UEBhMCQlIx
+ETAPBgNVBAgMCEJyYXNpbGlhMREwDwYDVQQHDAhCcmFzaWxpYTEOMAwGA1UECgwF
+VGVzdGUxDjAMBgNVBAsMBVRlc3RlMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIp7iRgRg9iTsI3nym2yZ6AlNZyZ/O
+lslcBCcZfOhMPKQXpipTMnV2cnSLlPQrrb0Oc4SjbIHfmIAQRF2UbihDV/ox6Osd
+V5Pim61o4IK4IeUlQnIg+zo/AyE9BC2eHq87YkYr8t0hmEywMk5yfvRgBdl6R7lc
+/kQ63TjBED0LINggKPkFlsvmgFlv5JVDGAJRgEbnfrV4k1AsIfArvCynSpuEpeSV
+dOaDf1lJoO7Dab+gln/aOq7QeI4BYR+R2X1bDgIRIa4HJnN5n10yMotVF5Q27O6d
+2hW13V8Wo/qpPYCOqiGoy//iCmb7JWPc2h7tR2wES6I9vl8ic5lDn1yTAgMBAAGj
+UDBOMB0GA1UdDgQWBBSpO3vqoG9vRq78kBDFEsUEvZA4wjAfBgNVHSMEGDAWgBSp
+O3vqoG9vRq78kBDFEsUEvZA4wjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUA
+A4IBAQDG2mBc3A0lZ3iM0oK5FiJN46LeJbicFPGgh2iiYsG+GOSXRfDEY/DMeEfL
+ukZCEcAUBygFxisCPR9rs5ReJIuNl7apH687OgiuSqC5OKPxcAMDgY4ECbqcBWp7
+sPfJfG9RZskwhn+yZpnyBOGGarpzpBTa8sahAppGCugSvy+Dpo4xOqiDRwByEa93
+Vzfb6pWBBeqw4i2z4XqctkqPh1pqnjdRpg0vgjwJLxFxsMXzpQq6vNkmSBSnAgnC
+L5lZ4ZcHV2hMyupToHaNEaJYw6O7SPNZr4+ODcPFHg3QwRyXtXypqh8y02Z6Fia+
+OMBZ5rFnNokcXiRSKhFJnWVivg1u
+-----END CERTIFICATE-----

+ 8 - 0
test/dh.pem

@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAkMksyk6ldPy+OqcR5Zo3L5dQUEgKmx6TkZaKxNnRnuisZ/f4fezh
+ArxDMmamAtOQeJ4oqLUjJUM3l/oFSmx60BgBo68oGRlN987Wudt1RJOMPnmbPZml
+mPzzP2jzh3phOYU3aWpuRHhi5sQPfQlfqrZ68kt40eBU8lZFGv28IQU6ZOLK344c
+RWuONPBTNi1NhbKQ9dPDw6yEE55mmSVKuS1rtGPX/ZX450vH0YrUlN5Aio9sRH49
+OM+kXliaCnpRFnWNcTqHSyKS+YCUahbsERuH9wW6mJU5oUtrzVApBIrAdqOy/Jsp
+oNEuIPXPdKxoNmaCQz7kuLotUhEF9lwPiwIBAg==
+-----END DH PARAMETERS-----

+ 28 - 0
test/key.pem

@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIp7iRgRg9iTsI
+3nym2yZ6AlNZyZ/OlslcBCcZfOhMPKQXpipTMnV2cnSLlPQrrb0Oc4SjbIHfmIAQ
+RF2UbihDV/ox6OsdV5Pim61o4IK4IeUlQnIg+zo/AyE9BC2eHq87YkYr8t0hmEyw
+Mk5yfvRgBdl6R7lc/kQ63TjBED0LINggKPkFlsvmgFlv5JVDGAJRgEbnfrV4k1As
+IfArvCynSpuEpeSVdOaDf1lJoO7Dab+gln/aOq7QeI4BYR+R2X1bDgIRIa4HJnN5
+n10yMotVF5Q27O6d2hW13V8Wo/qpPYCOqiGoy//iCmb7JWPc2h7tR2wES6I9vl8i
+c5lDn1yTAgMBAAECggEAGrqM0YUaz4eqP4IzpJ5ov7PzmnBLQ1++ru02sCR9ZTpm
+uWe/F5oPH5obgER1SiQ4nOYycvqPSlOsK36P4KfT4TSk9WULfLsfzf3i2aeeghyx
+w4bYYSBWH/KwyMn3sapwuT5cDpqpbkI1ZPsui/3xW6Er2SgYvxR3zkeNudoJ56NZ
+2pi4EhD9lF+jM6IfBmZ6UWi8vdtppLBP0Yfi3+CNE57//C1ozWPRlsuP7DeGpxWe
+voY4z4xZHnIULfK9JCfw5mTRI3gPn4/3z3eW9tqXE/ZkR1mkYrZ2UbJxe1MiR9jb
+P0YG6pX+SX1hlFXggs/IrKayKHMMD4B8tgPvyhlMgQKBgQD0ESobv4Z84+aNJpVW
+Ke3J229wim0Hn6bu88Aa3oEj4wJf7vgUiX4UxTCKa1mdpLszpgWHZTY60TxxE6Tw
+Ouc2eiXp1zUIMlzd5nTPNIeANQWt8HrAL5JWDaxXM0IPV9c+Wlpc4tTm3gdW6isr
+0mT5B12pnhRQLIwlqeErIPvMswKBgQDSdzJljKUrItJY8kuXSkSmXxpzhg9KBmCM
+GMeEfb/nRgKR8Gz6QAaRJ1Ax0IMr3Dmqabw2v5WHj/GcfO1q5m1ZSMf4FB6kjU6d
++menUGAXxvCjinr8grlC90koy/a4u/0g8mmCe5KMgmirQhT/ErfnqNHsprGlY+7U
+0NGZpr3goQKBgEOMRJUtarFB+dry5L3WGOOXAb6p8Qb9Hpxfhblp/1/JQiz+3FYC
+v4xMDuUgVMsWfmEK/9i7IEkjA5FgGHQMOVWQdNHFJ/4+wgj/8TAvn5jSE+JR/gcW
+o2+BlUMFArFwZDfzlbLDbJ0AshNR9+TG8/8gFMIO6BxQV/FMlO70z/uVAoGBAKQY
+n8ihol5BpjQHpnxtQZ1eZWdRTTZwRnK1F0rsKOYPpg1XogB0Typ5toNAiiV2bde6
+3S7qrZGm38Edfpds1jFZF+EK/uFZ88Qk6xB/EI05ZYJ9hGrBGeVmnTob3WAn4rL/
+jthXtOms/CMbQPeoBo+vBw424ieMBTkVH3dnlIBBAoGAe0HX47WU0T1jJY+UvCCJ
+1r88gBunrtD3ckFBX2Hk6hXXfTQd7Bsk5S/Cm3+zi2it5xS16KVdgQaTX1H9UASv
+hoJ6+pOfJYPGtMW+VWvMbqwJqSa6QKk7F0r5uyU8J51mlEQrKmhMY3xlNv3OGAaS
+ZM1G/mlQJFNv1WfLgc77Tes=
+-----END PRIVATE KEY-----

+ 1 - 0
test/testFile

@@ -0,0 +1 @@
+abcde

+ 16 - 2
uniform-io.cabal

@@ -113,13 +113,27 @@ library
   C-Sources: src/System/IO/Uniform/ds.c
   extra-libraries: ssl
 
-Test-suite lazyness
+Test-suite targets
   type: detailed-0.9
-  test-module: Lazyness
+  test-module: Targets
   hs-source-dirs:
     test
   build-depends:
     base >=4.7 && <5.0,
     Cabal >= 1.9.2,
+    bytestring >=0.10 && <1.0,
+    uniform-io == 0.1.1.0
+  ghc-options: -Wall -fno-warn-unused-do-bind -fwarn-incomplete-patterns -threaded
+
+Test-suite blocking
+  type: detailed-0.9
+  test-module: Blocking
+  hs-source-dirs:
+    test
+  build-depends:
+    base >=4.7 && <5.0,
+    Cabal >= 1.9.2,
+    bytestring >=0.10 && <1.0,
+    attoparsec >=0.10 && <1.0,
     uniform-io == 0.1.1.0
   ghc-options: -Wall -fno-warn-unused-do-bind -fwarn-incomplete-patterns -threaded