namecache: DNS caching proxy with P2P pseudo-subdomains
This program represents a proof-of-concept of a KadC application: the
implementation of a non-hierarchical "parallel DNS" where top-level
pseudodomains (TLPD's) are allocated on a "land grab" basis.
Information retrieved that way is completely insecure for a number of
reasons, and should only be used as "tip from the street" to be
separately verified through other channels. For instance, one could use
it to get the current IP address of a VoIP or IM peer given a textual
nym expressable as domain name, but the session should then be
independently authenticated (e.g. biometrically, or with cryptographic
techniques).
Due to its nature, namecache should NEVER be made work as an
authoritative server. Instead, it should be restricted to handle only
DNS queries coming from a local LAN or, better, from the same
machine (e.g. by binding to 127.0.0.1). Mixing cache and authoritative
server functionalities is always a bad idea (see
e.g.http://cr.yp.to/djbdns/separation.html ) but in this particular
case it would clash with the semantics expected from the DNS.
namecache may operate as a conventional DNS cache, listening for DNS
queries on the port 53 of a local machine (typically the same where the
application is running). The incoming queries are only accepted if they
contain exactly one question (this seems to happen always in desktop
applications); if they don't, a response with a "Not Implemented" rcode
is sent back to the client. Each acceptable query is looked up in a
local cache (implemented through a set of malloc'd records indexed both
by creation time and query body, skipping the header) and, if a cache
hit is obtained, the associated response is retrieved and used to
prepare a cached response. In case of a miss, the cache will place the
query in a "pending" table and will also forward a copy to the upstream
name servers. The first reply from the name servers will be forwarded
to the client and inserted into an in-memory cache (with an expiration
time depending on TTL etc). Cache entries found to be stale during a
cache query will be expunged; also, if a cache insertion will hit the
ceiling of cache_maxentries (as set by the "-c" option), the entry with
the earliest expiration time will be expunged to make room.
If the P2P functionality is used, occurrences of the -t option define
strings to be used as "top level (pseudo-)domain". Queries for domains
under those tlpd's, instead of being forwarded to the upstream servers,
are diverted to the P2P "resolver", which performs a KadC query. The -d
option allows to publish on the P2P subsystem a pseudo-domain that will
be "seen" by other copies of namecache connected to the Internet, and
will persist for a few hours after disconnection.
Complete list of command-line options:
-h
help: prints a short usage message
-k kadc_init_file
specifies the initialization file for the KadC engine
-c cache_maxentries
the maximum number of DNS entries (real and P2P). Defaults to
4096.
-p local_port
UDP port number to bind to in order to receive DNS queries.
Defaults to 53. Changing it to a non-privileged port (not lower than
1024) allows to run the program as non-root, but requires the use of
some system-dependent external tool in order to forward to that port
packets addressed to the port 53 (e.g., iptables under Linux kernels
2.4 or higher).
-i local_ip
IP address to bind to in order to receive DNS queries. Must be
one associated to a local interface, including the "loopback" 127.0.0.1
(in which case the DNS queries will only be accepted from the local
machine). Default: 0.0.0.0 . NOTE: this is independent from the address
to which the KadC library binds for its UDP traffic, which is defined
in the KadC ini file referenced by the "-k" option.
-s DNS_ipaddress
IP address of an upstream DNS server. Up to 4 can be specified
(through separate "-s" options).
-t tlpd
top-level pseudo-domain to be resolved by KadC with P2P queries.
E.g. "-t abcd" will result in all queries for "host1.abcd",
"www.sub1.abcd" etc. being intercepted and resolved with P2P queries
rather than passed to the upstream DNS servers. Up to 100 can be
specified (through separate "-t" options).
-d pseudodomain[=ipaddress]
pseudo-domain to be published through KadC as an A record
associated to the sub-parameter following the '=' character, if any, or
else the external IP address of this machine. The external address is
the one of the NAT device; KadC has primitives to intercept UDP packets
sent to its port of that address, effectively tunneling other UDP-based
protocols such as RTP or SIP. NOTE: pseudodomain is a fully qualified
domain name, not a subdomain of tlpd.
-u user
user under which the program is run after binding to a
privileged port (typically, the default 53). This allows to drop the
privileges to non-root (see below). In order for this option to work,
the program must be started as root. This option is not available on
the WIN32 platform.
-g group
group under which the program is run after binding to a
privileged port (typically, the default 53); the GID is retrieved from
the groups file (/etc/group). This option is normally specified
together with -u; if -g is not given and -u is, the GID will be
retrieved from the password file (/etc/passwd) entry for that user. In
order for this option to work, the program must be started as root.
This option is not available on the WIN32 platform.
Examples of use:
./namecache -s ns1.netscape.com -s ns1.yahoo.com -i 127.0.0.1
(Pure DNS cache with forwarding to two upstream nameservers, no
P2P pseudo-DNS at all. The resolver of the local machine should point
to 127.0.0.1 as single nameserver).
./namecache -s ns1.yahoo.com -k kadc.ini -t kad -d www.myd.p2p
(DNS cache with upstream forwarding for domains different from
*.kad ; the latter is resolved via P2P queries through the KadC
library, configured in the kadc.ini file. An A record associating the
pseudo-domain www.myd.p2p to the external address of the local machine
is published to the P2P overlay network handled by KadC, and will be
visible by other namecache programs running with the "-t p2p" option.)
./namecache -s ns1.yahoo.com -k kadc.ini -t kad -t p2p -d
www.myd.p2p
(As above, but specifying for P2P resolution two pseudo-TLD:
*.kad and *.p2p)
./namecache -s ns1.yahoo.com -k kadc.ini -t p2p -d x.p2p -d
y.p2p=1.2.3.4
(As above, but specifying for P2P resolution two pseudodomains:
x.p2p, associated to the external address, and y.p2p, explicitly
associated to 1.2.3.4)
Signals:
The program intercepts SIGINT and SIGTERM, and reacts to the
first occurrence of any of them with an orderly shutdown (subsequent
occurrences have no effect).
Background operations:
No console input is required by namecache, so the program may be
detached from console just by redirecting stdout and stderr to a file.
For instance, under *NIX putting in the rc.local the line:
/home/someuser/main/namecache -s 202.67.240.221 -s 202.67.240.222
-i 192.168.0.128 -u nobody -g nobody < /dev/null
>/var/log/namecache 2>&1 &
...results in the program being started in background upon
bootstrap and safely run as "nobody:nobody".
Security notes:
On UNIX-like platforms, namecache must be started with root privileges
in order to bind to the port 53 (of course, other DNS servers cannot be
run at the same time on the same machine). If the user (unwisely)
decides that the program should retain root privilege, should at least
run it chroot'd; this is made easier if namecache is statically linked,
in which case it will only need the presence of the kadc.ini file in
the same chroot jail. This will be made much easier and more secure
using tools such as Peter Hendrickson's Chroot Builder
(http://www.wiredyne.com/software/chrootbuilder.html ).
Another possibility is to bind namecache to a non-privileged port
through the option "-p" (e.g., "-p 5353") and forward the packets from
the port 53 to it using some system tool. With Linux kernels 2.2, that
can be achieved using ipchains:
ipchains -I input --proto UDP --dport 53 -j REDIRECT 5353
With 2.4 kernels and above, one can use iptables:
iptables -t nat -A PREROUTING -p udp --dport 53 -i eth0 -j
REDIRECT --to-port 5353
Unfortunately, that won't work for local traffic.
Finally, the "-u" and "-g" options mentioned above allow to specify a
non-root user and a group to which the program should fall back after
binding to a privileged port. This is the cleanest solution, but of
course such user and group should be created with very limited
privileges, and /bin/false as shell.