summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBinJin <>2019-06-11 11:32:00 (GMT)
committerhdiff <hdiff@hdiff.luite.com>2019-06-11 11:32:00 (GMT)
commitf0b1258c00c6e79a4d29d899130549760e6f9f59 (patch)
treefc8994b99128213fc1346c65b26c754c49139413
parent6c30e2088a76496edca8314ab65fb15ece7ca190 (diff)
version 0.1.20.1.2
-rw-r--r--README.md46
-rw-r--r--dprox.cabal44
-rw-r--r--src/Config.hs53
-rw-r--r--src/Main.hs14
4 files changed, 105 insertions, 52 deletions
diff --git a/README.md b/README.md
index 7038c80..bba4e7b 100644
--- a/README.md
+++ b/README.md
@@ -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).
+
+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`.
-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.
+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
diff --git a/dprox.cabal b/dprox.cabal
index 349756d..17cd0cb 100644
--- a/dprox.cabal
+++ b/dprox.cabal
@@ -4,10 +4,10 @@ cabal-version: 1.12
--
-- 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 @@ executable dprox
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-suite dprox-test
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
diff --git a/src/Config.hs b/src/Config.hs
index c97b46a..998d64b 100644
--- a/src/Config.hs
+++ b/src/Config.hs
@@ -7,6 +7,7 @@ module Config
, Config(..)
, getConfig
, IP(..)
+, invalidIPAddress
, PortNumber
) where
@@ -18,7 +19,8 @@ import qualified Data.Attoparsec.ByteString.Char8 as P8
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 @@ getConfig = do
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 @@ parseConfigFile bs = case P.parseOnly parseFile bs of
parseConfig =
parsePair "server" serverValue <|>
+ parsePair "local" serverValue <|>
parsePair "address" addressValue <|>
parsePair "bogus-nxdomain" bogusNXValue
@@ -115,6 +118,9 @@ ip = (<?> "IP address") $ do
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 @@ globalOption = GlobalConfig <$> userOption
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 @@ plainOption = (++) <$> many server <*> ((++) <$> many address <*> many bogusnx)
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 @@ attoparsecReader p = eitherReader (P.parseOnly (p <* P.endOfInput) . BS8.pack)
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
diff --git a/src/Main.hs b/src/Main.hs
index 459338c..6ae0c31 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -7,10 +7,9 @@ module Main where
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 @@ processDNS resolver bs
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 @@ main = do
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 @@ main = do
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'