From 8f5716c876f96be640539a3af129ed0a02cdcd85 Mon Sep 17 00:00:00 2001 From: Baylac-Jacqué Félix Date: Sat, 16 Sep 2017 15:35:34 +0200 Subject: Plumbed RPC set parser to STM state. --- src/Network/WireGuard/RPC.hs | 106 ++++++++------------------------ tests/spec/Network/WireGuard/RPCSpec.hs | 20 +++++- 2 files changed, 42 insertions(+), 84 deletions(-) diff --git a/src/Network/WireGuard/RPC.hs b/src/Network/WireGuard/RPC.hs index 6875332..0175127 100644 --- a/src/Network/WireGuard/RPC.hs +++ b/src/Network/WireGuard/RPC.hs @@ -10,52 +10,33 @@ module Network.WireGuard.RPC ) where import Control.Concurrent.STM (STM, atomically, - modifyTVar', readTVar, - writeTVar) -import Control.Monad (when) + readTVar, writeTVar) +import Control.Monad (when, unless) import Control.Monad.IO.Class (liftIO) import qualified Crypto.Noise.DH as DH (dhPubToBytes, dhSecToBytes, - dhBytesToPair, dhBytesToPair, - dhBytesToPub) -import Crypto.Noise.DH.Curve25519 (Curve25519) + dhBytesToPair, dhBytesToPair) import qualified Data.ByteArray as BA (convert) import qualified Data.ByteString as BS (ByteString, concat, - replicate, empty) -import Data.ByteString.Lazy (fromStrict) -import Data.ByteString.Conversion (toByteString') + empty) import qualified Data.ByteString.Char8 as BC (pack, singleton, map) import Data.Char (toLower) import Data.Conduit.Attoparsec (sinkParserEither) import Data.Conduit.Network.Unix (appSink, appSource, runUnixServer, serverSettings) -import qualified Data.HashMap.Strict as HM (delete, lookup, insert, - empty, elems, member) +import qualified Data.HashMap.Strict as HM (delete, lookup, insert, empty, + elems, member) import Data.Hex (hex) -import Data.Int (Int32) import Data.List (foldl') -import Data.Bits (Bits(..)) import Data.Conduit (ConduitM, (.|), yield, runConduit) -import Data.IP (IPRange(..), addrRangePair, - toHostAddress, toHostAddress6, - fromHostAddress, makeAddrRange, - fromHostAddress6) import Data.Maybe (fromJust, isJust, fromMaybe) - -import Network.WireGuard.Foreign.UAPI (WgPeer(..), WgDevice(..), - WgIpmask(..), - peerFlagRemoveMe, peerFlagReplaceIpmasks, - deviceFlagRemoveFwmark, deviceFlagReplacePeers, - deviceFlagRemovePrivateKey, deviceFlagRemovePresharedKey) -import Network.WireGuard.Internal.Constant (keyLength) import Network.WireGuard.Internal.RpcParsers (requestParser) import Network.WireGuard.Internal.State (Device(..), Peer(..), - createPeer, - invalidateSessions) + createPeer) import Network.WireGuard.Internal.Data.Types (PrivateKey, PublicKey, - PresharedKey, KeyPair) + KeyPair) import Network.WireGuard.Internal.Data.RpcTypes (RpcRequest(..), RpcSetPayload(..), OpType(..), RpcDevicePayload(..), RpcPeerPayload(..)) @@ -93,7 +74,7 @@ setDevice req dev = do when (isJust $ fwMark devReq) . writeTVar (fwmark dev) . fromJust $ fwMark devReq when (replacePeers devReq) $ delDevPeers dev let peersList = peersPayload . fromJust $ payload req - when (not $ null peersList) $ setPeers peersList dev + unless (null peersList) $ setPeers peersList dev return Nothing -- TODO: Handle errors using errno.h @@ -104,30 +85,25 @@ setPeers peerList dev = mapM_ inFunc peerList statePeers <- readTVar $ peers dev let peerPubK = pubToString $ pubK peer let peerExists = HM.member peerPubK statePeers - if remove peer + if remove peer && peerExists then removePeer peer dev - else if peerExists - then do - stmPeer <- modifyPeer peer (fromJust $ HM.lookup peerPubK statePeers) - let nPeers = HM.insert peerPubK stmPeer statePeers - writeTVar (peers dev) nPeers - else do - stmPeer <- createSTMPeer peer - let nPeers = HM.insert peerPubK stmPeer statePeers - writeTVar (peers dev) nPeers - -modifyPeer :: RpcPeerPayload -> Peer -> STM Peer -modifyPeer peer stmPeer = undefined - -createSTMPeer :: RpcPeerPayload -> STM Peer -createSTMPeer peer = do - stmPeer <- createPeer $ pubK peer + else do + stmPeer <- if peerExists + then return . fromJust $ HM.lookup peerPubK statePeers + else createPeer $ pubK peer + modifySTMPeer peer stmPeer + let nPeers = HM.insert peerPubK stmPeer statePeers + writeTVar (peers dev) nPeers + +modifySTMPeer :: RpcPeerPayload -> Peer -> STM () +modifySTMPeer peer stmPeer = do + stmPIps <- if replaceIps peer + then return [] + else readTVar $ ipmasks stmPeer writeTVar (endPoint stmPeer) . Just $ endpoint peer writeTVar (keepaliveInterval stmPeer) $ persistantKeepaliveInterval peer - writeTVar (ipmasks stmPeer) $ allowedIp peer - return stmPeer + writeTVar (ipmasks stmPeer) $ stmPIps ++ allowedIp peer - delDevPeers :: Device -> STM () delDevPeers dev = writeTVar (peers dev) HM.empty @@ -138,7 +114,7 @@ removePeer peer dev = do writeTVar (peers dev) nPeers showDevice :: Device -> STM BS.ByteString -showDevice device@Device{..} = do +showDevice Device{..} = do listen_port <- BC.pack . show <$> readTVar port fwm <- BC.pack . show <$> readTVar fwmark private_key <- fmap (toLowerBs . hex . privToBytes . fst) <$> readTVar localKey @@ -152,7 +128,6 @@ showDevice device@Device{..} = do showPeer :: Peer -> STM BS.ByteString showPeer Peer{..} = do - let hm = HM.empty let public_key = pubToString remotePub endpoint <- readTVar endPoint persistant_keepalive_interval <- readTVar keepaliveInterval @@ -179,25 +154,6 @@ serializeRpcKeyValue = foldl' showKeyValueLine BS.empty | otherwise = BS.concat [acc, BC.pack key, BC.singleton '=', val, BC.singleton '\n'] showKeyValueLine acc (_, Nothing) = acc - - -ipRangeToWgIpmask :: IPRange -> WgIpmask -ipRangeToWgIpmask (IPv4Range ipv4range) = case addrRangePair ipv4range of - (ipv4, prefix) -> WgIpmask (Left (toHostAddress ipv4)) (fromIntegral prefix) -ipRangeToWgIpmask (IPv6Range ipv6range) = case addrRangePair ipv6range of - (ipv6, prefix) -> WgIpmask (Right (toHostAddress6 ipv6)) (fromIntegral prefix) - -wgIpmaskToIpRange :: WgIpmask -> IPRange -wgIpmaskToIpRange (WgIpmask ip cidr) = case ip of - Left ipv4 -> IPv4Range $ makeAddrRange (fromHostAddress ipv4) (fromIntegral cidr) - Right ipv6 -> IPv6Range $ makeAddrRange (fromHostAddress6 ipv6) (fromIntegral cidr) - -invalidValueError :: Int32 -invalidValueError = 22 -- TODO: report back actual error - -emptyKey :: BS.ByteString -emptyKey = BS.replicate keyLength 0 - pubToBytes :: PublicKey -> BS.ByteString pubToBytes = BA.convert . DH.dhPubToBytes @@ -207,20 +163,8 @@ pubToString = toLowerBs . hex . pubToBytes privToBytes :: PrivateKey -> BS.ByteString privToBytes = BA.convert . DH.dhSecToBytes -pskToBytes :: PresharedKey -> BS.ByteString -pskToBytes = BA.convert - bytesToPair :: BS.ByteString -> Maybe KeyPair bytesToPair = DH.dhBytesToPair . BA.convert -bytesToPub :: BS.ByteString -> Maybe PublicKey -bytesToPub = DH.dhBytesToPub . BA.convert - -bytesToPSK :: BS.ByteString -> PresharedKey -bytesToPSK = BA.convert - toLowerBs :: BS.ByteString -> BS.ByteString toLowerBs = BC.map toLower - -testFlag :: Bits a => a -> a -> Bool -testFlag a flag = (a .&. flag) /= zeroBits diff --git a/tests/spec/Network/WireGuard/RPCSpec.hs b/tests/spec/Network/WireGuard/RPCSpec.hs index aae0678..a69a9b5 100644 --- a/tests/spec/Network/WireGuard/RPCSpec.hs +++ b/tests/spec/Network/WireGuard/RPCSpec.hs @@ -82,18 +82,32 @@ spec = do dev `shouldBe` BCL.pack "private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\nfwmark=1\npublic_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33\nendpoint=[abcd:23::33%2]:51820\nallowed_ip=192.168.4.4/32\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\nallowed_ip=192.168.4.6/32\nerrno=0\n\n" it "must repond to a correctly formed set device's peers V1 request with one peer remove instruction" $ do pk <- unhex $ BC.pack "e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a" - let devStm = getSetTestDevice pk 777 1 + pubKey1 <- unhex $ BC.pack "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33" + pubKey2 <- unhex $ BC.pack "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376" + pubKey3 <- unhex $ BC.pack "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58" + peer1 <- atomically $ getPeer1 pubKey1 + peer2 <- atomically $ getPeer2 pubKey2 + peer3 <- atomically $ getPeer3 pubKey3 + let devStm = getSetTestDeviceWithPeers pk 777 1 [(BC.pack "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33",peer1),(BC.pack "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376",peer2),(BC.pack "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58",peer3)] dev <- atomically devStm + err <- runConduit (yield (BC.pack "set=1\nprivate_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\npublic_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33\nremove=true\nendpoint=[abcd:23::33%2]:51820\nallowed_ip=192.168.4.4/32\n\n") .| serveConduit dev .| sinkLbs) + err `shouldBe` BCL.pack "errno=0\n\n" + dev <- runConduit (yield (BC.pack "get=1\n\n") .| serveConduit dev .| sinkLbs) + dev `shouldBe` BCL.pack "private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\nfwmark=1\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\ntx_bytes=1212111\nrx_bytes=1929999999\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\ntx_bytes=38333\nrx_bytes=2224\nallowed_ip=192.168.4.6/32\nerrno=0\n\n" + it "must repond to a correctly formed set device's peers V1 request with one peer alter instruction" $ do + pk <- unhex $ BC.pack "e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a" pubKey1 <- unhex $ BC.pack "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33" pubKey2 <- unhex $ BC.pack "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376" pubKey3 <- unhex $ BC.pack "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58" peer1 <- atomically $ getPeer1 pubKey1 peer2 <- atomically $ getPeer2 pubKey2 peer3 <- atomically $ getPeer3 pubKey3 - err <- runConduit (yield (BC.pack "set=1\nprivate_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\npublic_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33\nremove=true\nendpoint=[abcd:23::33%2]:51820\nallowed_ip=192.168.4.4/32\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\nallowed_ip=192.168.4.6/32\n\n") .| serveConduit dev .| sinkLbs) + let devStm = getSetTestDeviceWithPeers pk 777 1 [(BC.pack "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33",peer1),(BC.pack "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376",peer2),(BC.pack "662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58",peer3)] + dev <- atomically devStm + err <- runConduit (yield (BC.pack "set=1\nprivate_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\npublic_key=b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33\nremove=true\nendpoint=[abcd:23::33%2]:51820\nallowed_ip=192.168.4.4/32\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\nallowed_ip=192.168.5.6/32\n\n") .| serveConduit dev .| sinkLbs) err `shouldBe` BCL.pack "errno=0\n\n" dev <- runConduit (yield (BC.pack "get=1\n\n") .| serveConduit dev .| sinkLbs) - dev `shouldBe` BCL.pack "private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\nfwmark=1\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\nallowed_ip=192.168.4.6/32\nerrno=0\n\n" + dev `shouldBe` BCL.pack "private_key=e84b5a6d2717c1003a13b431570353dbaca9146cf150c5f8575680feba52027a\nlisten_port=12912\nfwmark=1\npublic_key=662e14fd594556f522604703340351258903b64f35553763f19426ab2a515c58\nendpoint=5.152.198.39:51820\ntx_bytes=1212111\nrx_bytes=1929999999\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\nallowed_ip=192.168.4.10/32\nallowed_ip=192.168.4.11/32\npublic_key=58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376\nendpoint=182.122.22.19:3233\npersistent_keepalive_interval=111\ntx_bytes=38333\nrx_bytes=2224\nallowed_ip=192.168.4.6/32\nallowed_ip=192.168.5.6/32\nerrno=0\n\n" it "must repond to a correctly formed delete device's peers set V1 request" $ do pubKey1 <- unhex $ BC.pack "b85996fecc9c7f1fc6d2572a76eda11d59bcd20be8e543b15ce4bd85a8e75a33" pubKey2 <- unhex $ BC.pack "58402e695ba1772b1cc9309755f043251ea77fdcf10fbe63989ceb7e19321376" -- cgit v1.2.3-59-g8ed1b