aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2021-11-08 18:59:47 +0100
committerDaniel Schadt <kingdread@gmx.de>2021-11-08 18:59:47 +0100
commit6c41185fd189de4242749b6dd787099a37270c68 (patch)
treef9390b518728507227632a5d29ff93ab29b3f470 /src
parentc2827483f9d92c7abbc71a93c5a7607642826f1c (diff)
downloadCana-6c41185fd189de4242749b6dd787099a37270c68.tar.gz
Cana-6c41185fd189de4242749b6dd787099a37270c68.tar.bz2
Cana-6c41185fd189de4242749b6dd787099a37270c68.zip
reply 59 on proxy requestsHEADmaster
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.hs2
-rw-r--r--src/Cana/Router.hs26
-rw-r--r--src/Cana/Server.hs16
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 = ""
+ })