@@ -22,6 +22,8 @@ module Network.Connection
22
22
23
23
-- * Exceptions
24
24
, LineTooLong (.. )
25
+ , HostNotResolved (.. )
26
+ , HostCannotConnect (.. )
25
27
26
28
-- * Library initialization
27
29
, initConnectionContext
@@ -58,7 +60,8 @@ import System.X509 (getSystemCertificateStore)
58
60
59
61
import Network.Socks5
60
62
import qualified Network as N
61
- import Network.Socket (Socket )
63
+ import Network.Socket
64
+ import Network.BSD (getProtocolNumber )
62
65
import qualified Network.Socket as N (close )
63
66
import qualified Network.Socket.ByteString as N
64
67
@@ -81,7 +84,15 @@ type Manager = MVar (M.Map TLS.SessionID TLS.SessionData)
81
84
-- the line in ConnectionGetLine.
82
85
data LineTooLong = LineTooLong deriving (Show ,Typeable )
83
86
87
+ -- | Exception raised when there's no resolution for a specific host
88
+ data HostNotResolved = HostNotResolved String deriving (Show ,Typeable )
89
+
90
+ -- | Exception raised when the connect failed
91
+ data HostCannotConnect = HostCannotConnect String [E. IOException ] deriving (Show ,Typeable )
92
+
84
93
instance E. Exception LineTooLong
94
+ instance E. Exception HostNotResolved
95
+ instance E. Exception HostCannotConnect
85
96
86
97
connectionSessionManager :: Manager -> TLS. SessionManager
87
98
connectionSessionManager mvar = TLS. SessionManager
@@ -159,22 +170,22 @@ connectTo :: ConnectionContext -- ^ The global context of this connection.
159
170
connectTo cg cParams = do
160
171
conFct <- getConFct (connectionUseSocks cParams)
161
172
h <- conFct (connectionHostname cParams) (N. PortNumber $ connectionPort cParams)
162
- connectFromHandle cg h cParams
173
+ connectFromSocket cg h cParams
163
174
where
164
- getConFct Nothing = return N. connectTo
165
- getConFct (Just (OtherProxy h p)) = return $ \ _ _ -> N. connectTo h (N. PortNumber p)
166
- getConFct (Just (SockSettingsSimple h p)) = return $ socksConnectTo h (N. PortNumber p)
175
+ getConFct Nothing = return resolve'
176
+ getConFct (Just (OtherProxy h p)) = return $ \ _ _ -> resolve' h (N. PortNumber p)
177
+ getConFct (Just (SockSettingsSimple h p)) = return $ socksConnectTo' h (N. PortNumber p)
167
178
getConFct (Just (SockSettingsEnvironment v)) = do
168
179
-- if we can't get the environment variable or that the variable cannot be parsed
169
180
-- we connect directly.
170
181
let name = maybe " SOCKS_SERVER" id v
171
182
evar <- E. try (getEnv name)
172
183
case evar of
173
- Left (_ :: E. IOException ) -> return N. connectTo
184
+ Left (_ :: E. IOException ) -> return resolve'
174
185
Right var ->
175
186
case parseSocks var of
176
- Nothing -> return N. connectTo
177
- Just (sHost, sPort) -> return $ socksConnectTo sHost (N. PortNumber $ fromIntegral (sPort :: Int ))
187
+ Nothing -> return resolve'
188
+ Just (sHost, sPort) -> return $ socksConnectTo' sHost (N. PortNumber $ fromIntegral (sPort :: Int ))
178
189
179
190
-- Try to parse "host:port" or "host"
180
191
parseSocks s =
@@ -186,6 +197,34 @@ connectTo cg cParams = do
186
197
_ -> Nothing
187
198
_ -> Nothing
188
199
200
+ resolve' host portid = do
201
+ let serv = case portid of
202
+ N. Service serv -> serv
203
+ N. PortNumber n -> show n
204
+ _ -> error " cannot resolve service"
205
+ proto <- getProtocolNumber " tcp"
206
+ let hints = defaultHints { addrFlags = [AI_ADDRCONFIG ]
207
+ , addrProtocol = proto
208
+ , addrSocketType = Stream }
209
+ addrs <- getAddrInfo (Just hints) (Just host) (Just serv)
210
+ firstSuccessful $ map tryToConnect addrs
211
+ where
212
+ tryToConnect addr =
213
+ E. bracketOnError
214
+ (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
215
+ (N. close)
216
+ (\ sock -> connect sock (addrAddress addr) >> return sock)
217
+ firstSuccessful = go []
218
+ where
219
+ go :: [E. IOException ] -> [IO a ] -> IO a
220
+ go [] [] = E. throwIO $ HostNotResolved host
221
+ go l@ (_: _) [] = E. throwIO $ HostCannotConnect host l
222
+ go acc (act: followingActs) = do
223
+ er <- E. try act
224
+ case er of
225
+ Left err -> go (err: acc) followingActs
226
+ Right r -> return r
227
+
189
228
-- | Put a block of data in the connection.
190
229
connectionPut :: Connection -> ByteString -> IO ()
191
230
connectionPut connection content = withBackend doWrite connection
0 commit comments