packages feed

dprox 0.1.1 → 0.1.2

raw patch · 4 files changed

+105/−52 lines, 4 filesdep ~attoparsecdep ~basedep ~bytestring

Dependency ranges changed: attoparsec, base, bytestring, containers, iproute, network, optparse-applicative, streaming-commons, unix, unordered-containers

Files

README.md view
@@ -7,23 +7,59 @@ [![AUR](https://img.shields.io/aur/version/dprox.svg)](https://aur.archlinux.org/packages/dprox/) [![License](https://img.shields.io/github/license/bjin/dprox.svg)](https://github.com/bjin/dprox/blob/master/LICENSE) -dprox is a lightweight DNS proxy server. It's written as a drop-in replacement-of dnsmasq to work with [dnsmasq-china-list](https://github.com/felixonmars/dnsmasq-china-list),+`dprox` is a lightweight DNS proxy server. It's created as a drop-in replacement+of [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) to work with+[dnsmasq-china-list](https://github.com/felixonmars/dnsmasq-china-list), while improving the overall lookup performance over large domain list.  ### Installation -Only Linux and macOS are supported. [stack](https://docs.haskellstack.org/en/stable/README/#how-to-install) is required to build `dprox`.+`dprox` should build and work on all unix-like OS with [ghc](https://www.haskell.org/ghc/) support, but it's only+been tested on Linux and macOS. +While `dprox` can be built with [cabal](https://www.haskell.org/cabal/) like any other Hackage packages, for a+reliable compilation with pinned dependencies, [stack](https://docs.haskellstack.org/en/stable/README/#how-to-install) is generally recommended.+ ```sh+stack setup stack install ``` +For Arch Linux users, an [AUR package](https://aur.archlinux.org/packages/dprox/) is also provided.+ ### Usage -Only a small subset of dnsmasq options are implemented at the moment, just barely enough to work with `dnsmasq-china-list`.+Only a small subset of dnsmasq options are implemented at the moment, just barely enough to work with `dnsmasq-china-list` and [hosts-blocklists](https://github.com/notracking/hosts-blocklists). -Use `dprox --help` to list those options. A [systemd unit file](https://github.com/bjin/dprox/blob/master/systemd/dprox.service) is also provided for Linux user.+Here is the list of implemented dnsmasq options (with `server`, `local`, `address` and `bogus-nxdomain` options allowed in configuration file):++```+-u, --user=<username>+-p, --port=<port>+-a, --listen-address=<ipaddr>+-C, --conf-file=<file>+-h, --no-hosts+-H, --addn-hosts=<file>+-S, --local, --server=[/<domain>/]<ipaddr>[#<port>]+-A, --address=[/<domain>/]<ipaddr>+-B, --bogus-nxdomain=<ipaddr>+```++Use `dprox --help` or [dnsmasq manpage](http://www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html) for further details about these options.+But be aware that there might be minor differences on some options like `--server`.++To use `dprox` with `dnsmasq-china-list`, with "8.8.4.4" as the remote DNS server+(systemd user can also use [this unit file](https://github.com/bjin/dprox/blob/master/systemd/dprox.service)):++```sh+dprox -C /etc/dnsmasq.d/accelerated-domains.china.conf -C /etc/dnsmasq.d/bogus-nxdomain.china.conf -S 8.8.4.4+```++To use `dprox` with `hosts-blocklists` and the default remote DNS server ("8.8.8.8"), without loading system hosts file:++```sh+dprox -C /opt/hosts-blocklists/domains.txt -H /opt/hosts-blocklists/hostnames.txt -h+```  ### Known Issue 
dprox.cabal view
@@ -4,10 +4,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: d262889761a247d0874e1aea677d066dce6b02883a49661fd85293dd83929b03+-- hash: e6d3163f54b56ac9d5fc87fae25c5446de14a1af8a295aa09b2db319888e28d7  name:           dprox-version:        0.1.1+version:        0.1.2 synopsis:       a lightweight DNS proxy server description:    Please see the README on GitHub at <https://github.com/bjin/dprox#readme> category:       DNS@@ -41,17 +41,17 @@       src   ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N   build-depends:-      attoparsec-    , base >=4.7 && <5-    , bytestring-    , containers+      attoparsec >=0.13+    , base >=4.12 && <5+    , bytestring >=0.10+    , containers >=0.6     , dns >=3.0.4-    , iproute-    , network-    , optparse-applicative-    , streaming-commons-    , unix-    , unordered-containers+    , iproute >=1.7+    , network >=2.8 && <3+    , optparse-applicative >=0.14+    , streaming-commons >=0.2+    , unix >=2.7+    , unordered-containers >=0.2   if flag(static)     ghc-options: -optl-static   default-language: Haskell2010@@ -66,16 +66,16 @@       test   ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N   build-depends:-      attoparsec-    , base >=4.7 && <5-    , bytestring-    , containers+      attoparsec >=0.13+    , base >=4.12 && <5+    , bytestring >=0.10+    , containers >=0.6     , dns >=3.0.4     , hspec-    , iproute-    , network-    , optparse-applicative-    , streaming-commons-    , unix-    , unordered-containers+    , iproute >=1.7+    , network >=2.8 && <3+    , optparse-applicative >=0.14+    , streaming-commons >=0.2+    , unix >=2.7+    , unordered-containers >=0.2   default-language: Haskell2010
src/Config.hs view
@@ -7,6 +7,7 @@ , Config(..) , getConfig , IP(..)+, invalidIPAddress , PortNumber ) where @@ -18,7 +19,8 @@ import qualified Data.ByteString                  as BS import qualified Data.ByteString.Char8            as BS8 import           Data.IP                          (IP (..))-import           Data.Maybe                       (catMaybes)+import           Data.Maybe                       (catMaybes, fromMaybe,+                                                   isNothing) import           Data.Streaming.Network           (HostPreference) import           Data.String                      (fromString) import qualified Network.DNS                      as DNS@@ -47,7 +49,7 @@   where     opts = info ((,,,) <$> globalOption <*> configFileOption                        <*> hostsFilesOption <*> plainOption <**> helper)-      ( fullDesc <> progDesc "a simple DNS proxy server")+      ( fullDesc <> progDesc "a lightweight DNS proxy server, supports a small subset of dnsmasq options")      readConfigFromFile file = handle handler (parseConfigFile <$> BS.readFile file)     readHostsFromFile file = handle handler (parseHostsFile <$> BS.readFile file)@@ -64,6 +66,7 @@      parseConfig =         parsePair "server" serverValue <|>+        parsePair "local" serverValue <|>         parsePair "address" addressValue <|>         parsePair "bogus-nxdomain" bogusNXValue @@ -115,6 +118,9 @@         Nothing     -> fail ("invalid IP address: " ++ ipstr)         Just ipaddr -> return ipaddr +invalidIPAddress :: IP+invalidIPAddress = "::"+ port :: P.Parser PortNumber port = read . BS8.unpack <$> P.takeWhile1 P8.isDigit_w8 <?> "Port Number" @@ -126,39 +132,39 @@     userOption = optional $ strOption         ( long "user"        <> short 'u'-       <> metavar "uid"-       <> help "set user id")+       <> metavar "<username>"+       <> help "Specify the userid to which dprox will change after startup")      portOption = optional $ option auto         ( long "port"        <> short 'p'-       <> metavar "port"-       <> help "listen on this port instead of 53")+       <> metavar "<port>"+       <> help "Listen on this port instead of 53")      listenOption = optional $ fromString <$> strOption         ( long "listen-address"        <> short 'a'-       <> metavar "ipaddr"+       <> metavar "<ipaddr>"        <> help "Listen on the given IP address")  configFileOption :: Parser [FilePath] configFileOption = many $ strOption     ( long "conf-file"    <> short 'C'-   <> metavar "path/to/dprox.conf"-   <> help "configure file to read")+   <> metavar "<file>"+   <> help "Configure file to read")  hostsFilesOption :: Parser [FilePath] hostsFilesOption = combine <$> noHostsOption <*> many newHostsOption   where     combine False newHosts = "/etc/hosts" : newHosts-    combine True newHosts = newHosts+    combine True newHosts  = newHosts      newHostsOption = strOption         ( long "addn-hosts"        <> short 'H'-       <> metavar "path/to/hosts"-       <> help "additional hosts file to read (other than /etc/hosts)")+       <> metavar "<file>"+       <> help "Additional hosts file to read other than /etc/hosts")      noHostsOption = switch         ( long "no-hosts"@@ -170,21 +176,29 @@   where     server = option (attoparsecReader serverValue)         ( long "server"+       <> long "local"        <> short 'S'-       <> metavar "[/domain/]ip[#port]"-       <> help "remote dns server ip")+       <> metavar "[/<domain>/]<ipaddr>[#<port>]"+       <> help serverMsg) +    serverMsg = "Specify remote DNS server to use. " +++                "If multiple servers are specified, only the last one will be used. " +++                "If no server is specified, 8.8.8.8 will be used. " +++                "If <domain> is specified, queries matching this domain or its subdomains will use use specified remote DNS server. " +++                "If <ipaddr> is empty, queries matching specified domains will be handled by local hosts file only. " +++                "<port> can be used to specify alternative port for DNS server."+     address = option (attoparsecReader addressValue)         ( long "address"        <> short 'A'-       <> metavar "/domain/ip"-       <> help "specifiy ip for target domain")+       <> metavar "[/<domain>/]<ipaddr>"+       <> help "For DNS queries matching <domain> or its subdomains, replies <ipaddr> directly")      bogusnx = option (attoparsecReader bogusNXValue)         ( long "bogus-nxdomain"        <> short 'B'        <> metavar "ip"-       <> help "specify ip for no such domain blacklist")+       <> help "Transform replies which contain the IP address given into \"No such domain\" replies")  attoparsecReader :: P.Parser a -> ReadM a attoparsecReader p = eitherReader (P.parseOnly (p <* P.endOfInput) . BS8.pack)@@ -192,9 +206,10 @@ serverValue :: P.Parser Config serverValue = do     parsedDomain <- P.option Nothing (Just <$> (P8.char '/' *> domain <* P8.char '/'))-    parsedIP <- ip+    parsedIP <- P.option Nothing (Just <$> ip)+    when (isNothing parsedDomain && isNothing parsedIP) $ fail "at least one of <domain> and <ip> must be specified"     parsedPort <- P.option Nothing (Just <$> (P8.char '#' *> port))-    return (Server parsedDomain parsedIP parsedPort)+    return (Server parsedDomain (fromMaybe invalidIPAddress parsedIP) parsedPort)  addressValue :: P.Parser Config addressValue = Address <$> (P8.char '/' *> domain <* P8.char '/') <*> ip
src/Main.hs view
@@ -7,10 +7,9 @@  import           Control.Concurrent        (forkIO) import           Control.Exception         (SomeException, handle)-import           Control.Monad             (forM, forever)+import           Control.Monad             (forM, forever, join) import           Data.ByteString           (ByteString) import qualified Data.Foldable             as F-import           Data.Map                  ((!)) import qualified Data.Map                  as M import           Data.Maybe                (fromMaybe) import qualified Data.Set                  as S@@ -67,10 +66,12 @@         q <- DNS.decode bs         if DNS.qOrR (DNS.flags (DNS.header q)) == DNS.QR_Query then return q else Left DNS.FormatError -handleServer :: DomainRoute Resolver -> Resolver-handleServer route qd = resolver qd+handleServer :: DomainRoute (Maybe Resolver) -> Resolver+handleServer route qd qt = case resolver of+    Nothing        -> return (Left DNS.NameError)+    Just resolver' -> resolver' qd qt   where-    resolver = fromMaybe (error "handleServer: internal error") (getDomainRouteByPrefix route qd)+    resolver = join (getDomainRouteByPrefix route qd)  handleAddressAndHosts :: DomainRoute [IP] -> DomainRoute [IP] -> Resolver -> Resolver handleAddressAndHosts address hosts resolver qd qt =@@ -122,6 +123,7 @@          resolvConfs = [ (addr, rc)                       | addr@(host, port) <- S.toList serverAddressSet+                      , host /= invalidIPAddress                       , let rsinfo = if port == defaultPort                                 then DNS.RCHostName (show host)                                 else DNS.RCHostPort (show host) port@@ -146,7 +148,7 @@          createResolvers ((k,v):xs) m = DNS.withResolver v $ \rs ->             createResolvers xs (M.insert k (DNS.lookup rs) m)-        createResolvers [] m = let serverRoute' = fmap (m!) serverRoute+        createResolvers [] m = let serverRoute' = fmap (`M.lookup`m) serverRoute                                    resolver = handleBogusNX bogusnxSet $                                               handleAddressAndHosts addressRoute hostsRoute $                                               handleServer serverRoute'