@@ -28,8 +28,8 @@ import System.Socket.Family.Inet
28
28
import System.Socket.Type.Stream
29
29
import System.Socket.Protocol.TCP
30
30
import System.Socket.Family.Unix
31
- import Data.Time.Clock.POSIX
32
31
import Control.Concurrent.Chan.Unagi
32
+ import Crypto.Hash (hash , Digest , MD5 )
33
33
34
34
import Database.PostgreSQL.Protocol.Encoders
35
35
import Database.PostgreSQL.Protocol.Decoders
@@ -62,6 +62,10 @@ data Error
62
62
| ImpossibleError
63
63
deriving (Show )
64
64
65
+ data AuthError
66
+ = AuthPostgresError ErrorDesc
67
+ | AuthNotSupported B. ByteString
68
+
65
69
data DataMessage = DataMessage [V. Vector B. ByteString ]
66
70
deriving (Show )
67
71
@@ -118,11 +122,13 @@ connect :: ConnectionSettings -> IO Connection
118
122
connect settings = do
119
123
rawConn <- createRawConnection settings
120
124
when (settingsTls settings == RequiredTls ) $ handshakeTls rawConn
121
- authorize rawConn settings
125
+ authResult <- authorize rawConn settings
126
+ -- TODO should close connection on error
127
+ connParams <- either (error " invalid connection" ) pure authResult
122
128
123
129
(inDataChan, outDataChan) <- newChan
124
- (inAllChan, outAllChan) <- newChan
125
- storage <- newStatementStorage
130
+ (inAllChan, outAllChan) <- newChan
131
+ storage <- newStatementStorage
126
132
127
133
tid <- forkIO $ receiverThread rawConn inDataChan inAllChan
128
134
pure Connection
@@ -131,18 +137,62 @@ connect settings = do
131
137
, connOutDataChan = outDataChan
132
138
, connOutAllChan = outAllChan
133
139
, connStatementStorage = storage
134
- , connParameters = ConnectionParameters
135
- { paramServerVersion = ServerVersion 1 1 1
136
- , paramServerEncoding = " "
137
- , paramIntegerDatetimes = True
138
- }
140
+ , connParameters = connParams
139
141
}
140
142
141
- authorize :: RawConnection -> ConnectionSettings -> IO ()
143
+ authorize
144
+ :: RawConnection
145
+ -> ConnectionSettings
146
+ -> IO (Either AuthError ConnectionParameters )
142
147
authorize rawConn settings = do
143
148
sendStartMessage rawConn $ consStartupMessage settings
149
+ -- 4096 should be enough for the whole response from a server at
150
+ -- startup phase.
144
151
r <- rReceive rawConn 4096
145
- readAuthMessage r
152
+ case pushChunk (runGetIncremental decodeAuthResponse) r of
153
+ BG. Done rest _ r -> case r of
154
+ AuthenticationOk -> do
155
+ putStrLn " Auth ok"
156
+ -- TODO parse parameters
157
+ pure $ Right $ parseParameters rest
158
+ AuthenticationCleartextPassword ->
159
+ performPasswordAuth $ PasswordPlain $ settingsPassword settings
160
+ AuthenticationMD5Password (MD5Salt salt) ->
161
+ let pass = " md5" <> md5Hash (md5Hash (settingsPassword settings
162
+ <> settingsUser settings) <> salt)
163
+ in performPasswordAuth $ PasswordMD5 pass
164
+ AuthenticationGSS -> pure $ Left $ AuthNotSupported " GSS"
165
+ AuthenticationSSPI -> pure $ Left $ AuthNotSupported " SSPI"
166
+ AuthenticationGSSContinue _ -> pure $ Left $ AuthNotSupported " GSS"
167
+ AuthErrorResponse desc -> pure $ Left $ AuthPostgresError desc
168
+ -- TODO handle this case
169
+ f -> error " athorize"
170
+ where
171
+ performPasswordAuth
172
+ :: PasswordText -> IO (Either AuthError ConnectionParameters )
173
+ performPasswordAuth password = do
174
+ putStrLn $ " sending password" ++ show password
175
+ sendMessage rawConn $ PasswordMessage password
176
+ r <- rReceive rawConn 4096
177
+ case pushChunk (runGetIncremental decodeAuthResponse) r of
178
+ BG. Done rest _ r -> case r of
179
+ AuthenticationOk -> do
180
+ putStrLn " Auth ok"
181
+ pure $ Right $ parseParameters rest
182
+ AuthErrorResponse desc ->
183
+ pure $ Left $ AuthPostgresError desc
184
+ _ -> error " Impossible happened"
185
+ -- TODO handle this case
186
+ f -> error " authorize"
187
+ -- TODO right parsing
188
+ parseParameters :: B. ByteString -> ConnectionParameters
189
+ parseParameters str = ConnectionParameters
190
+ { paramServerVersion = ServerVersion 1 1 1
191
+ , paramIntegerDatetimes = False
192
+ , paramServerEncoding = " "
193
+ }
194
+ md5Hash :: B. ByteString -> B. ByteString
195
+ md5Hash bs = BS. pack $ show (hash bs :: Digest MD5 )
146
196
147
197
handshakeTls :: RawConnection -> IO ()
148
198
handshakeTls _ = pure ()
@@ -166,13 +216,6 @@ sendMessage rawConn msg = void $ do
166
216
let smsg = toStrict . toLazyByteString $ encodeClientMessage msg
167
217
rSend rawConn smsg
168
218
169
- readAuthMessage :: B. ByteString -> IO ()
170
- readAuthMessage s =
171
- case pushChunk (runGetIncremental decodeAuthResponse) s of
172
- BG. Done _ _ r -> case r of
173
- AuthenticationOk -> putStrLn " Auth ok"
174
- _ -> error " Invalid auth"
175
- f -> error $ show s
176
219
177
220
receiverThread
178
221
:: RawConnection
0 commit comments