packages feed

crypton-connection (empty) → 0.3.1

raw patch · 7 files changed

+690/−0 lines, 7 filesdep +basedep +basementdep +bytestringsetup-changed

Dependencies added: base, basement, bytestring, containers, crypton-x509, crypton-x509-store, crypton-x509-system, crypton-x509-validation, data-default-class, network, socks, tls

Files

+ CHANGELOG.md view
@@ -0,0 +1,7 @@+## Version 0.2.1 (16 April 2014)++- Fix a difference between TLSSettings and TLSSettingsSimple,+  where connection would override the connection hostname and port in+  the simple case, but leave the field as is with TLSSettings.+  TLSSettings can now be used properly as template, and will be+  correctly overriden at the identification level only.
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2012-2019 Vincent Hanquez <vincent@snarc.org>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ Network/Connection.hs view
@@ -0,0 +1,427 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ScopedTypeVariables #-}+-- |+-- Module      : Network.Connection+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : portable+--+-- Simple connection abstraction+--+module Network.Connection+    (+    -- * Type for a connection+      Connection+    , connectionID+    , ConnectionParams(..)+    , TLSSettings(..)+    , ProxySettings(..)+    , SockSettings++    -- * Exceptions+    , LineTooLong(..)+    , HostNotResolved(..)+    , HostCannotConnect(..)++    -- * Library initialization+    , initConnectionContext+    , ConnectionContext++    -- * Connection operation+    , connectFromHandle+    , connectFromSocket+    , connectTo+    , connectionClose++    -- * Sending and receiving data+    , connectionGet+    , connectionGetExact+    , connectionGetChunk+    , connectionGetChunk'+    , connectionGetLine+    , connectionWaitForInput+    , connectionPut++    -- * TLS related operations+    , connectionSetSecure+    , connectionIsSecure+    , connectionSessionManager+    ) where++import Control.Concurrent.MVar+import Control.Monad (join)+import qualified Control.Exception as E+import qualified System.IO.Error as E (mkIOError, eofErrorType)++import qualified Network.TLS as TLS+import qualified Network.TLS.Extra as TLS++import System.X509 (getSystemCertificateStore)++import Network.Socks5 (defaultSocksConf, socksConnectWithSocket, SocksAddress(..), SocksHostAddress(..))+import Network.Socket+import qualified Network.Socket.ByteString as N++import Data.Tuple (swap)+import Data.Default.Class+import Data.Data+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Lazy as L++import System.Environment+import System.Timeout+import System.IO+import qualified Data.Map as M++import Network.Connection.Types++type Manager = MVar (M.Map TLS.SessionID TLS.SessionData)++-- | This is the exception raised if we reached the user specified limit for+-- the line in ConnectionGetLine.+data LineTooLong = LineTooLong deriving (Show,Typeable)++-- | Exception raised when there's no resolution for a specific host+data HostNotResolved = HostNotResolved String deriving (Show,Typeable)++-- | Exception raised when the connect failed+data HostCannotConnect = HostCannotConnect String [E.IOException] deriving (Show,Typeable)++instance E.Exception LineTooLong+instance E.Exception HostNotResolved+instance E.Exception HostCannotConnect++connectionSessionManager :: Manager -> TLS.SessionManager+connectionSessionManager mvar = TLS.SessionManager+    { TLS.sessionResume     = \sessionID -> withMVar mvar (return . M.lookup sessionID)+    , TLS.sessionEstablish  = \sessionID sessionData ->+                               modifyMVar_ mvar (return . M.insert sessionID sessionData)+    , TLS.sessionInvalidate = \sessionID -> modifyMVar_ mvar (return . M.delete sessionID)+#if MIN_VERSION_tls(1,5,0)+    , TLS.sessionResumeOnlyOnce = \sessionID ->+         modifyMVar mvar (pure . swap . M.updateLookupWithKey (\_ _ -> Nothing) sessionID)+#endif+    }++-- | Initialize the library with shared parameters between connection.+initConnectionContext :: IO ConnectionContext+initConnectionContext = ConnectionContext <$> getSystemCertificateStore++-- | Create a final TLS 'ClientParams' according to the destination and the+-- TLSSettings.+makeTLSParams :: ConnectionContext -> ConnectionID -> TLSSettings -> TLS.ClientParams+makeTLSParams cg cid ts@(TLSSettingsSimple {}) =+    (TLS.defaultParamsClient (fst cid) portString)+        { TLS.clientSupported = def { TLS.supportedCiphers = TLS.ciphersuite_default }+        , TLS.clientShared    = def+            { TLS.sharedCAStore         = globalCertificateStore cg+            , TLS.sharedValidationCache = validationCache+            -- , TLS.sharedSessionManager  = connectionSessionManager+            }+        }+  where validationCache+            | settingDisableCertificateValidation ts =+                TLS.ValidationCache (\_ _ _ -> return TLS.ValidationCachePass)+                                    (\_ _ _ -> return ())+            | otherwise = def+        portString = BC.pack $ show $ snd cid+makeTLSParams _ cid (TLSSettings p) =+    p { TLS.clientServerIdentification = (fst cid, portString) }+ where portString = BC.pack $ show $ snd cid++withBackend :: (ConnectionBackend -> IO a) -> Connection -> IO a+withBackend f conn = readMVar (connectionBackend conn) >>= f++connectionNew :: ConnectionID -> ConnectionBackend -> IO Connection+connectionNew cid backend =+    Connection <$> newMVar backend+               <*> newMVar (Just B.empty)+               <*> pure cid++-- | Use an already established handle to create a connection object.+--+-- if the TLS Settings is set, it will do the handshake with the server.+-- The SOCKS settings have no impact here, as the handle is already established+connectFromHandle :: ConnectionContext+                  -> Handle+                  -> ConnectionParams+                  -> IO Connection+connectFromHandle cg h p = withSecurity (connectionUseSecure p)+    where withSecurity Nothing            = connectionNew cid $ ConnectionStream h+          withSecurity (Just tlsSettings) = tlsEstablish h (makeTLSParams cg cid tlsSettings) >>= connectionNew cid . ConnectionTLS+          cid = (connectionHostname p, connectionPort p)++-- | Use an already established handle to create a connection object.+--+-- if the TLS Settings is set, it will do the handshake with the server.+-- The SOCKS settings have no impact here, as the handle is already established+connectFromSocket :: ConnectionContext+                  -> Socket+                  -> ConnectionParams+                  -> IO Connection+connectFromSocket cg sock p = withSecurity (connectionUseSecure p)+    where withSecurity Nothing            = connectionNew cid $ ConnectionSocket sock+          withSecurity (Just tlsSettings) = tlsEstablish sock (makeTLSParams cg cid tlsSettings) >>= connectionNew cid . ConnectionTLS+          cid = (connectionHostname p, connectionPort p)++-- | Connect to a destination using the parameter+connectTo :: ConnectionContext -- ^ The global context of this connection.+          -> ConnectionParams  -- ^ The parameters for this connection (where to connect, and such).+          -> IO Connection     -- ^ The new established connection on success.+connectTo cg cParams = do+    let conFct = doConnect (connectionUseSocks cParams)+                           (connectionHostname cParams)+                           (connectionPort cParams)+    E.bracketOnError conFct (close . fst) $ \(h, _) ->+        connectFromSocket cg h cParams+  where+    sockConnect sockHost sockPort h p = do+        (sockServ, servAddr) <- resolve' sockHost sockPort+        let sockConf = defaultSocksConf servAddr+        let destAddr = SocksAddress (SocksAddrDomainName $ BC.pack h) p+        (dest, _) <- socksConnectWithSocket sockServ sockConf destAddr+        case dest of+            SocksAddrIPV4 h4 -> return (sockServ, SockAddrInet p h4)+            SocksAddrIPV6 h6 -> return (sockServ, SockAddrInet6 p 0 h6 0)+            SocksAddrDomainName _ -> error "internal error: socks connect return a resolved address as domain name"+++    doConnect proxy h p =+        case proxy of+            Nothing                 -> resolve' h p+            Just (OtherProxy proxyHost proxyPort) -> resolve' proxyHost proxyPort+            Just (SockSettingsSimple sockHost sockPort) ->+                sockConnect sockHost sockPort h p+            Just (SockSettingsEnvironment envName) -> do+                -- if we can't get the environment variable or that the string cannot be parsed+                -- we connect directly.+                let name = maybe "SOCKS_SERVER" id envName+                evar <- E.try (getEnv name)+                case evar of+                    Left (_ :: E.IOException) -> resolve' h p+                    Right var                 ->+                        case parseSocks var of+                            Nothing                   -> resolve' h p+                            Just (sockHost, sockPort) -> sockConnect sockHost sockPort h p++    -- Try to parse "host:port" or "host"+    -- if port is omitted then the default SOCKS port (1080) is assumed+    parseSocks :: String -> Maybe (String, PortNumber)+    parseSocks s =+        case break (== ':') s of+            (sHost, "")        -> Just (sHost, 1080)+            (sHost, ':':portS) ->+                case reads portS of+                    [(sPort,"")] -> Just (sHost, sPort)+                    _            -> Nothing+            _                  -> Nothing++    -- Try to resolve the host/port to an address (zero to many of them), then+    -- try to connect from the first address to the last, returning the first one that+    -- succeeds+    resolve' :: String -> PortNumber -> IO (Socket, SockAddr)+    resolve' host port = do+        let hints = defaultHints { addrFlags = [AI_ADDRCONFIG], addrSocketType = Stream }+        addrs <- getAddrInfo (Just hints) (Just host) (Just $ show port)+        firstSuccessful $ map tryToConnect addrs+      where+        tryToConnect addr =+            E.bracketOnError+                (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))+                (close)+                (\sock -> connect sock (addrAddress addr) >> return (sock, addrAddress addr))+        firstSuccessful = go []+          where+            go :: [E.IOException] -> [IO a] -> IO a+            go []      [] = E.throwIO $ HostNotResolved host+            go l@(_:_) [] = E.throwIO $ HostCannotConnect host l+            go acc     (act:followingActs) = do+                er <- E.try act+                case er of+                    Left err -> go (err:acc) followingActs+                    Right r  -> return r++-- | Put a block of data in the connection.+connectionPut :: Connection -> ByteString -> IO ()+connectionPut connection content = withBackend doWrite connection+    where doWrite (ConnectionStream h) = B.hPut h content >> hFlush h+          doWrite (ConnectionSocket s) = N.sendAll s content+          doWrite (ConnectionTLS ctx)  = TLS.sendData ctx $ L.fromChunks [content]++-- | Get exact count of bytes from a connection.+--+-- The size argument is the exact amount that must be returned to the user.+-- The call will wait until all data is available.  Hence, it behaves like+-- 'B.hGet'.+--+-- On end of input, 'connectionGetExact' will throw an 'E.isEOFError'+-- exception.+connectionGetExact :: Connection -> Int -> IO ByteString+connectionGetExact conn x = loop B.empty 0+  where loop bs y+          | y == x = return bs+          | otherwise = do+            next <- connectionGet conn (x - y)+            loop (B.append bs next) (y + (B.length next))++-- | Get some bytes from a connection.+--+-- The size argument is just the maximum that could be returned to the user.+-- The call will return as soon as there's data, even if there's less+-- than requested.  Hence, it behaves like 'B.hGetSome'.+--+-- On end of input, 'connectionGet' returns 0, but subsequent calls will throw+-- an 'E.isEOFError' exception.+connectionGet :: Connection -> Int -> IO ByteString+connectionGet conn size+  | size < 0  = fail "Network.Connection.connectionGet: size < 0"+  | size == 0 = return B.empty+  | otherwise = connectionGetChunkBase "connectionGet" conn $ B.splitAt size++-- | Get the next block of data from the connection.+connectionGetChunk :: Connection -> IO ByteString+connectionGetChunk conn =+    connectionGetChunkBase "connectionGetChunk" conn $ \s -> (s, B.empty)++-- | Like 'connectionGetChunk', but return the unused portion to the buffer,+-- where it will be the next chunk read.+connectionGetChunk' :: Connection -> (ByteString -> (a, ByteString)) -> IO a+connectionGetChunk' = connectionGetChunkBase "connectionGetChunk'"++-- | Wait for input to become available on a connection.+--+-- As with 'hWaitForInput', the timeout value is given in milliseconds.  If the+-- timeout value is less than zero, then 'connectionWaitForInput' waits+-- indefinitely.+--+-- Unlike 'hWaitForInput', this function does not do any decoding, so it+-- returns true when there is /any/ available input, not just full characters.+connectionWaitForInput :: Connection -> Int -> IO Bool+connectionWaitForInput conn timeout_ms = maybe False (const True) <$> timeout timeout_ns tryGetChunk+  where tryGetChunk = connectionGetChunkBase "connectionWaitForInput" conn $ \buf -> ((), buf)+        timeout_ns  = timeout_ms * 1000++connectionGetChunkBase :: String -> Connection -> (ByteString -> (a, ByteString)) -> IO a+connectionGetChunkBase loc conn f =+    modifyMVar (connectionBuffer conn) $ \m ->+        case m of+            Nothing -> throwEOF conn loc+            Just buf+              | B.null buf -> do+                  chunk <- withBackend getMoreData conn+                  if B.null chunk+                     then closeBuf chunk+                     else updateBuf chunk+              | otherwise ->+                  updateBuf buf+  where+    getMoreData (ConnectionTLS tlsctx) = TLS.recvData tlsctx+    getMoreData (ConnectionSocket sock) = N.recv sock 1500+    getMoreData (ConnectionStream h)   = B.hGetSome h (16 * 1024)++    updateBuf buf = case f buf of (a, !buf') -> return (Just buf', a)+    closeBuf  buf = case f buf of (a, _buf') -> return (Nothing, a)++-- | Get the next line, using ASCII LF as the line terminator.+--+-- This throws an 'isEOFError' exception on end of input, and LineTooLong when+-- the number of bytes gathered is over the limit without a line terminator.+--+-- The actual line returned can be bigger than the limit specified, provided+-- that the last chunk returned by the underlaying backend contains a LF.+-- Put another way: Only when we need more input and limit is reached that the+-- LineTooLong exception will be raised.+--+-- An end of file will be considered as a line terminator too, if the line is+-- not empty.+connectionGetLine :: Int           -- ^ Maximum number of bytes before raising a LineTooLong exception+                  -> Connection    -- ^ Connection+                  -> IO ByteString -- ^ The received line with the LF trimmed+connectionGetLine limit conn = more (throwEOF conn loc) 0 id+  where+    loc = "connectionGetLine"+    lineTooLong = E.throwIO LineTooLong++    -- Accumulate chunks using a difference list and concatenate them+    -- when an end-of-line indicator is reached.+    more eofK !currentSz !dl =+        getChunk (\s -> let len = B.length s+                         in if currentSz + len > limit+                               then lineTooLong+                               else more eofK (currentSz + len) (dl . (s:)))+                 (\s -> done (dl . (s:)))+                 (done dl)++    done :: ([ByteString] -> [ByteString]) -> IO ByteString+    done dl = return $! B.concat $ dl []++    -- Get another chunk and call one of the continuations+    getChunk :: (ByteString -> IO r) -- moreK: need more input+             -> (ByteString -> IO r) -- doneK: end of line (line terminator found)+             -> IO r                 -- eofK:  end of file+             -> IO r+    getChunk moreK doneK eofK =+      join $ connectionGetChunkBase loc conn $ \s ->+        if B.null s+          then (eofK, B.empty)+          else case B.break (== 10) s of+                 (a, b)+                   | B.null b  -> (moreK a, B.empty)+                   | otherwise -> (doneK a, B.tail b)++throwEOF :: Connection -> String -> IO a+throwEOF conn loc =+    E.throwIO $ E.mkIOError E.eofErrorType loc' Nothing (Just path)+  where+    loc' = "Network.Connection." ++ loc+    path = let (host, port) = connectionID conn+            in host ++ ":" ++ show port++-- | Close a connection.+connectionClose :: Connection -> IO ()+connectionClose = withBackend backendClose+    where backendClose (ConnectionTLS ctx)  = ignoreIOExc (TLS.bye ctx) `E.finally` TLS.contextClose ctx+          backendClose (ConnectionSocket sock) = close sock+          backendClose (ConnectionStream h) = hClose h++          ignoreIOExc action = action `E.catch` \(_ :: E.IOException) -> return ()++-- | Activate secure layer using the parameters specified.+--+-- This is typically used to negotiate a TLS channel on an already+-- established channel, e.g., supporting a STARTTLS command. It also+-- flushes the received buffer to prevent application confusing+-- received data before and after the setSecure call.+--+-- If the connection is already using TLS, nothing else happens.+connectionSetSecure :: ConnectionContext+                    -> Connection+                    -> TLSSettings+                    -> IO ()+connectionSetSecure cg connection params =+    modifyMVar_ (connectionBuffer connection) $ \b ->+    modifyMVar (connectionBackend connection) $ \backend ->+        case backend of+            (ConnectionStream h) -> do ctx <- tlsEstablish h (makeTLSParams cg (connectionID connection) params)+                                       return (ConnectionTLS ctx, Just B.empty)+            (ConnectionSocket s) -> do ctx <- tlsEstablish s (makeTLSParams cg (connectionID connection) params)+                                       return (ConnectionTLS ctx, Just B.empty)+            (ConnectionTLS _)    -> return (backend, b)++-- | Returns if the connection is establish securely or not.+connectionIsSecure :: Connection -> IO Bool+connectionIsSecure conn = withBackend isSecure conn+    where isSecure (ConnectionStream _) = return False+          isSecure (ConnectionSocket _) = return False+          isSecure (ConnectionTLS _)    = return True++tlsEstablish :: TLS.HasBackend backend => backend -> TLS.ClientParams -> IO TLS.Context+tlsEstablish handle tlsParams = do+    ctx <- TLS.contextNew handle tlsParams+    TLS.handshake ctx+    return ctx
+ Network/Connection/Types.hs view
@@ -0,0 +1,100 @@+-- |+-- Module      : Network.Connection.Types+-- License     : BSD-style+-- Maintainer  : Vincent Hanquez <vincent@snarc.org>+-- Stability   : experimental+-- Portability : portable+--+-- connection types+--+module Network.Connection.Types+    where++import Control.Concurrent.MVar (MVar)++import Data.Default.Class+import Data.X509.CertificateStore+import Data.ByteString (ByteString)++import Network.Socket (PortNumber, Socket)+import qualified Network.TLS as TLS++import System.IO (Handle)++-- | Simple backend enumeration, either using a raw connection or a tls connection.+data ConnectionBackend = ConnectionStream Handle+                       | ConnectionSocket Socket+                       | ConnectionTLS TLS.Context+++-- | Hostname This could either be a name string (punycode encoded) or an ipv4/ipv6+type HostName = String++-- | Connection Parameters to establish a Connection.+--+-- The strict minimum is an hostname and the port.+--+-- If you need to establish a TLS connection, you should make sure+-- connectionUseSecure is correctly set.+--+-- If you need to connect through a SOCKS, you should make sure+-- connectionUseSocks is correctly set.+data ConnectionParams = ConnectionParams+    { connectionHostname   :: HostName           -- ^ host name to connect to.+    , connectionPort       :: PortNumber         -- ^ port number to connect to.+    , connectionUseSecure  :: Maybe TLSSettings  -- ^ optional TLS parameters.+    , connectionUseSocks   :: Maybe ProxySettings -- ^ optional Proxy/Socks configuration.+    }++-- | Proxy settings for the connection.+--+-- OtherProxy handles specific application-level proxies like HTTP proxies.+--+-- The simple SOCKS settings is just the hostname and portnumber of the SOCKS proxy server.+--+-- That's for now the only settings in the SOCKS package,+-- socks password, or any sort of other authentications is not yet implemented.+data ProxySettings =+      SockSettingsSimple HostName PortNumber+    | SockSettingsEnvironment (Maybe String)+    | OtherProxy HostName PortNumber++type SockSettings = ProxySettings++-- | TLS Settings that can be either expressed as simple settings,+-- or as full blown TLS.Params settings.+--+-- Unless you need access to parameters that are not accessible through the+-- simple settings, you should use TLSSettingsSimple.+data TLSSettings+    = TLSSettingsSimple+             { settingDisableCertificateValidation :: Bool -- ^ Disable certificate verification completely,+                                                           --   this make TLS/SSL vulnerable to a MITM attack.+                                                           --   not recommended to use, but for testing.+             , settingDisableSession               :: Bool -- ^ Disable session management. TLS/SSL connections+                                                           --   will always re-established their context.+                                                           --   Not Implemented Yet.+             , settingUseServerName                :: Bool -- ^ Use server name extension. Not Implemented Yet.+             } -- ^ Simple TLS settings. recommended to use.+    | TLSSettings TLS.ClientParams -- ^ full blown TLS Settings directly using TLS.Params. for power users.+    deriving (Show)++instance Default TLSSettings where+    def = TLSSettingsSimple False False False++type ConnectionID = (HostName, PortNumber)++-- | This opaque type represent a connection to a destination.+data Connection = Connection+    { connectionBackend :: MVar ConnectionBackend+    , connectionBuffer  :: MVar (Maybe ByteString) -- ^ this is set to 'Nothing' on EOF+    , connectionID      :: ConnectionID  -- ^ return a simple tuple of the port and hostname that we're connected to.+    }++-- | Shared values (certificate store, sessions, ..) between connections+--+-- At the moment, this is only strictly needed to shared sessions and certificates+-- when using a TLS enabled connection.+data ConnectionContext = ConnectionContext+    { globalCertificateStore :: !CertificateStore+    }
+ README.md view
@@ -0,0 +1,83 @@+haskell Connection library+==========================++Simple network library for all your connection need.++Features:++- Really simple to use+- SSL/TLS+- SOCKS++Usage+-----++Connect to www.example.com on port 4567 (without socks or tls), then send a+byte, receive a single byte, print it, and close the connection:+```haskell+import qualified Data.ByteString as B+import Network.Connection+import Data.Default++main = do+    ctx <- initConnectionContext+    con <- connectTo ctx $ ConnectionParams+                              { connectionHostname  = "www.example.com"+                              , connectionPort      = 4567+                              , connectionUseSecure = Nothing+                              , connectionUseSocks  = Nothing+                              }+    connectionPut con (B.singleton 0xa)+    r <- connectionGet con 1+    putStrLn $ show r+    connectionClose con+```+Using a socks proxy is easy, we just need replacing the connectionSocks+parameter, for example connecting to the same host, but using a socks+proxy at localhost:1080:+```haskell+con <- connectTo ctx $ ConnectionParams+                       { connectionHostname  = "www.example.com"+                       , connectionPort      = 4567+                       , connectionUseSecure = Nothing+                       , connectionUseSocks  = Just $ SockSettingsSimple "localhost" 1080+                       }+```+Connecting to a SSL style socket is equally easy, and need to set the UseSecure fields in ConnectionParams:+```haskell+con <- connectTo ctx $ ConnectionParams+                       { connectionHostname  = "www.example.com"+                       , connectionPort      = 4567+                       , connectionUseSecure = Just def+                       , connectionUseSocks  = Nothing+                       }+```+And finally, you can start TLS in the middle of an insecure connection. This is great for+protocol using STARTTLS (e.g. IMAP, SMTP):++```haskell+{-# LANGUAGE OverloadedStrings #-}+import qualified Data.ByteString as B+import Data.ByteString.Char8 ()+import Network.Connection+import Data.Default++main = do+    ctx <- initConnectionContext+    con <- connectTo ctx $ ConnectionParams+                              { connectionHostname  = "www.example.com"+                              , connectionPort      = 4567+                              , connectionUseSecure = Nothing+                              , connectionUseSocks  = Nothing+                              }+    -- talk to the other side with no TLS: says hello and starttls+    connectionPut con "HELLO\n"+    connectionPut con "STARTTLS\n"++    -- switch to TLS+    connectionSetSecure ctx con def++    -- the connection is from now on using TLS, we can send secret for example+    connectionPut con "PASSWORD 123\n"+    connectionClose con+```
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ crypton-connection.cabal view
@@ -0,0 +1,44 @@+Name:                crypton-connection+Version:             0.3.1+Description:+    Simple network library for all your connection need.+    .+    Features: Really simple to use, SSL/TLS, SOCKS.+    .+    This library provides a very simple api to create sockets+    to a destination with the choice of SSL/TLS, and SOCKS.+License:             BSD3+License-file:        LICENSE+Copyright:           Vincent Hanquez <vincent@snarc.org>+Author:              Vincent Hanquez <vincent@snarc.org>+Maintainer:          Kazu Yamamoto <kazu@iij.ad.jp>+Synopsis:            Simple and easy network connections API+Build-Type:          Simple+Category:            Network+stability:           experimental+Cabal-Version:       >=1.10+Homepage:            https://github.com/kazu-yamamoto/crypton-connection+extra-source-files:  README.md+                     CHANGELOG.md++Library+  Default-Language:  Haskell2010+  Build-Depends:     base >= 3 && < 5+                   , basement+                   , bytestring+                   , containers+                   , data-default-class+                   , network >= 2.6.3+                   , tls >= 1.7+                   , socks >= 0.6+                   , crypton-x509 >= 1.5+                   , crypton-x509-store >= 1.5+                   , crypton-x509-system >= 1.5+                   , crypton-x509-validation >= 1.5+  Exposed-modules:   Network.Connection+  Other-modules:     Network.Connection.Types+  ghc-options:       -Wall++source-repository head+  type: git+  location: https://github.com/kazu-yamamoto/crypton-connection