diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2021-11-08 18:59:47 +0100 | 
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2021-11-08 18:59:47 +0100 | 
| commit | 6c41185fd189de4242749b6dd787099a37270c68 (patch) | |
| tree | f9390b518728507227632a5d29ff93ab29b3f470 /src | |
| parent | c2827483f9d92c7abbc71a93c5a7607642826f1c (diff) | |
| download | Cana-master.tar.gz Cana-master.tar.bz2 Cana-master.zip  | |
This makes the server more compliant with what is to be expected and
prevents routes from matching when a wrong scheme is given (such as
https://).
It is not perfect yet, as abusing the route matching function for such
an important property is probably not the best. We also want to
implement other request-sanity-checks (such as port numbers being
given), which would also be better done at a different place.
However, we also don't want to prevent Cana from being used to implement
Gemini proxy servers, so a flag in GeminiServer would probably be nice
("extended URI checks").
Yet - on the completely other hand - allowing that much freedom also
means weird interactions (such as a default vhost, certificate issues,
...), so it is unclear how far we want to go.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Cana.hs | 2 | ||||
| -rw-r--r-- | src/Cana/Router.hs | 26 | ||||
| -rw-r--r-- | src/Cana/Server.hs | 16 | 
3 files changed, 40 insertions, 4 deletions
diff --git a/src/Cana.hs b/src/Cana.hs index 4d5c4d2..7aca4f2 100644 --- a/src/Cana.hs +++ b/src/Cana.hs @@ -185,7 +185,7 @@ findRoute req = do    vhosts <- asks serverVHosts    let hostName = maybe "" uriRegName . uriAuthority $ requestUri req        vhost    = findVHost hostName vhosts -      routes   = maybe [] vhostRoutes vhost +      routes   = maybe [denyProxyRoute] vhostRoutes vhost    return $ snd <$> findIt routes      where        findIt = find (\(pred, _) -> pred req) diff --git a/src/Cana/Router.hs b/src/Cana/Router.hs index 1454375..757e9c9 100644 --- a/src/Cana/Router.hs +++ b/src/Cana/Router.hs @@ -1,18 +1,42 @@  -- |  -- Module     : Cana.Router -module Cana.Router where +module Cana.Router +  ( -- * Predicate combinators +    (&&~) +  , (||~) +    -- * Predicates +  , anyRoute +  , geminiScheme +  , routeHost +  ) where  import Cana.Types  import Cana.Protocol  import Cana.Monad +import Control.Arrow  import Network.URI +infixr 3 &&~ +infixr 2 ||~ + +-- | A predicate that matches when both predicates match. +(&&~) :: Predicate -> Predicate -> Predicate +(&&~) a b = uncurry (&&) . (a &&& b) + +-- | A predicate that matches when one of the predicates matches. +(||~) :: Predicate -> Predicate -> Predicate +(||~) a b = uncurry (||) . (a &&& b) +  -- | A predicate that always triggers, e.g. for catch-all routes.  anyRoute :: Predicate  anyRoute = const True +-- | A predicate that matches the @gemini://@ scheme. +geminiScheme :: Predicate +geminiScheme = (== "gemini:") . uriScheme . requestUri +  -- | A predicate that matches the given hostname.  routeHost :: String -> Predicate  routeHost host request = case uriAuthority (requestUri request) of diff --git a/src/Cana/Server.hs b/src/Cana/Server.hs index 130de47..6f9cc50 100644 --- a/src/Cana/Server.hs +++ b/src/Cana/Server.hs @@ -14,12 +14,14 @@ module Cana.Server    , loadCredentials    , hostMatches    , findVHost +  , denyProxyRoute    ) where  import           Cana.Protocol  import           Cana.Types  import           Cana.Util  import           Cana.Handlers +import           Cana.Router  import           Control.Monad.Trans.Except  import           Control.Monad.IO.Class @@ -115,7 +117,7 @@ parseConfig input = case parse input of        return VirtualHost          { vhostName        = UTF8.toString host          , vhostCredentials = creds -        , vhostRoutes      = routes +        , vhostRoutes      = routes ++ [denyProxyRoute]          }      loadRoute :: Node -> ExceptT String IO Route @@ -125,7 +127,7 @@ parseConfig input = case parse input of        let predicate = (=~ match) . uriPath . requestUri        handler <- unwrapMaybe "No handler given for route" (listToMaybe $ children elem)          >>= makeHandler -      return (predicate, handler) +      return (geminiScheme &&~ predicate, handler)      makeHandler :: Node -> ExceptT String IO Handler      makeHandler elem = case name elem of @@ -185,3 +187,13 @@ hostMatches patt host = matches (prepare patt) (prepare host)  -- | Find the correct virtual host based on the hostname.  findVHost :: HostName -> [VirtualHost] -> Maybe VirtualHost  findVHost hostName = find (flip hostMatches hostName . vhostName) + + +-- | A route that matches non-gemini schemes and replies with +-- 'codeProxyRequestRefused'. +denyProxyRoute :: Route +denyProxyRoute = (not . geminiScheme, const $ return GeminiResponse +  { responseStatus = codeProxyRequestRefused +  , responseMeta   = "You may not proxy through this server" +  , responseData   = "" +  })  | 
