2018-04-09 23:04:13 +02:00
/* Goal: a fully standards compliant basic authoritative server. In <1000 lines.
2018-04-01 18:31:41 +02:00
Non - goals : notifications , slaving zones , name compression , edns ,
performance
*/
2018-04-16 17:09:27 +02:00
/*!
@ file
@ brief This is the main file of the tdns authoritative server
*/
2018-04-01 18:31:41 +02:00
# include <cstdint>
# include <vector>
# include <map>
# include <stdexcept>
# include "sclasses.hh"
2018-04-03 13:18:37 +02:00
# include <thread>
2018-04-09 12:54:27 +02:00
# include <signal.h>
2018-04-12 16:22:35 +02:00
# include "record-types.hh"
2018-04-08 23:20:11 +02:00
# include "dns-storage.hh"
2018-04-01 18:31:41 +02:00
using namespace std ;
2018-04-16 17:09:27 +02:00
/*! \mainpage Welcome to tdns
\ section Introduction
tdns is a simple authoritative nameserver that is fully faithful to the
DNS storage model as outlined in RFC 1034.
*/
2018-04-17 12:32:56 +02:00
void addAdditional ( const DNSNode * bestzone , const DNSName & zone , const vector < DNSName > & toresolve , DNSMessageWriter & response ) ;
2018-04-09 23:04:13 +02:00
2018-04-16 17:09:27 +02:00
/** \brief This is the main DNS logic function
This is the main ' DNS logic ' function . It receives a set of zones ,
a readable DNS query from a certain IP address , and a writable
DNS response .
This function is called by both UDP and TCP listeners . It therefore
does not do any IXFR / AXFR . It does however perform several sanity checks .
Returns false if no response should be sent .
This function implements " the algorithm " from RFC 1034 and is key to
unstanding DNS */
bool processQuestion ( const DNSNode & zones , DNSMessageReader & dm , const ComboAddress & remote , DNSMessageWriter & response )
2018-04-01 18:31:41 +02:00
{
2018-04-14 20:16:56 +02:00
if ( dm . dh . qr ) {
cerr < < " Dropping non-query from " < < remote . toStringWithPort ( ) < < endl ;
return false ; // should not send ANY kind of response, loop potential
}
2018-04-13 11:02:59 +02:00
DNSName qname ;
DNSType qtype ;
dm . getQuestion ( qname , qtype ) ;
2018-04-13 15:23:00 +02:00
2018-04-13 11:02:59 +02:00
DNSName origname = qname ; // we need this for error reporting, we munch the original name
cout < < " Received a query from " < < remote . toStringWithPort ( ) < < " for " < < qname < < " and type " < < qtype < < endl ;
2018-04-11 22:12:57 +02:00
try {
2018-04-13 17:48:32 +02:00
response . dh . id = dm . dh . id ; response . dh . rd = dm . dh . rd ;
2018-04-11 22:12:57 +02:00
response . dh . ad = response . dh . ra = response . dh . aa = 0 ;
2018-04-13 17:56:51 +02:00
response . dh . qr = 1 ; response . dh . opcode = dm . dh . opcode ;
2018-04-14 20:16:56 +02:00
2018-04-13 17:39:44 +02:00
uint16_t newsize ; bool doBit ;
if ( dm . getEDNS ( & newsize , & doBit ) ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Have EDNS, buffer size = " < < newsize < < " , DO bit = " < < doBit < < endl ;
2018-04-13 17:39:44 +02:00
if ( dm . d_ednsVersion ! = 0 ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Bad EDNS version: " < < ( int ) dm . d_ednsVersion < < endl ;
2018-04-13 17:39:44 +02:00
response . setEDNS ( newsize , doBit , RCode : : Badvers ) ;
return true ;
}
response . setEDNS ( newsize , doBit ) ;
}
2018-04-03 18:03:59 +02:00
2018-04-13 11:02:59 +02:00
if ( qtype = = DNSType : : AXFR | | qtype = = DNSType : : IXFR ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Query was for AXFR or IXFR over UDP, can't do that " < < endl ;
2018-04-11 22:12:57 +02:00
response . dh . rcode = ( int ) RCode : : Servfail ;
return true ;
}
2018-04-03 18:03:59 +02:00
2018-04-11 22:12:57 +02:00
if ( dm . dh . opcode ! = 0 ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Query had non-zero opcode " < < dm . dh . opcode < < " , sending NOTIMP " < < endl ;
2018-04-11 22:12:57 +02:00
response . dh . rcode = ( int ) RCode : : Notimp ;
return true ;
}
2018-04-16 17:09:27 +02:00
// find the best zone for this query
2018-04-13 11:02:59 +02:00
DNSName zonename ;
auto fnd = zones . find ( qname , zonename ) ;
2018-04-16 17:09:27 +02:00
if ( ! fnd | | ! fnd - > zone ) { // check if we found an actual zone
2018-04-17 12:32:56 +02:00
cout < < " \t No zone matched " < < endl ;
2018-04-13 11:02:59 +02:00
response . dh . rcode = ( uint8_t ) RCode : : Refused ;
return true ;
}
2018-04-16 17:09:27 +02:00
// qname is now relative to the zonename
2018-04-17 12:32:56 +02:00
cout < < " \t Found best zone: " < < zonename < < " , qname now " < < qname < < endl ;
2018-04-13 11:02:59 +02:00
response . dh . aa = 1 ;
2018-04-11 22:12:57 +02:00
2018-04-16 17:09:27 +02:00
auto bestzone = fnd - > zone . get ( ) ; // this loads a pointer to the zone contents
2018-04-13 11:02:59 +02:00
DNSName searchname ( qname ) , lastnode , zonecutname ;
const DNSNode * passedZonecut = 0 ;
int CNAMELoopCount = 0 ;
loopCNAME : ;
2018-04-16 17:09:27 +02:00
/* search for the best node, where we want to benefit from wildcard synthesis
note that this is the same ' find ' we used to find the best zone , but we did not
want any wildcard procssing there */
2018-04-14 00:12:53 +02:00
auto node = bestzone - > find ( searchname , lastnode , true , & passedZonecut , & zonecutname ) ;
2018-04-13 11:02:59 +02:00
if ( passedZonecut ) {
response . dh . aa = false ;
2018-04-17 12:32:56 +02:00
cout < < " \t This is a delegation, zonecutname: ' " < < zonecutname < < " ' " < < endl ;
2018-04-16 17:09:27 +02:00
auto iter = passedZonecut - > rrsets . find ( DNSType : : NS ) ; // is there an NS record here? should be!
2018-04-13 11:02:59 +02:00
if ( iter ! = passedZonecut - > rrsets . end ( ) ) {
const auto & rrset = iter - > second ;
2018-04-16 17:09:27 +02:00
2018-04-13 11:02:59 +02:00
vector < DNSName > toresolve ;
for ( const auto & rr : rrset . contents ) {
2018-04-16 17:09:27 +02:00
/* add the NS records to the authority section. Note that for this we have to make
the name absolute again : zonecutname + zonename */
2018-04-13 11:02:59 +02:00
response . putRR ( DNSSection : : Authority , zonecutname + zonename , DNSType : : NS , rrset . ttl , rr ) ;
2018-04-16 17:09:27 +02:00
// and add for additional processing
2018-04-13 11:02:59 +02:00
toresolve . push_back ( dynamic_cast < NSGen * > ( rr . get ( ) ) - > d_name ) ;
2018-04-03 18:03:59 +02:00
}
2018-04-13 11:02:59 +02:00
addAdditional ( bestzone , zonename , toresolve , response ) ;
2018-04-03 18:03:59 +02:00
}
2018-04-13 11:02:59 +02:00
}
2018-04-16 17:09:27 +02:00
else if ( ! searchname . empty ( ) ) { // we had parts of the qname that did not match
2018-04-17 12:32:56 +02:00
cout < < " \t This is an NXDOMAIN situation " < < endl ;
2018-04-13 11:02:59 +02:00
if ( ! CNAMELoopCount ) // RFC 1034, 4.3.2, step 3.c
2018-04-11 22:12:57 +02:00
response . dh . rcode = ( int ) RCode : : Nxdomain ;
2018-04-16 17:09:27 +02:00
const auto & rrset = bestzone - > rrsets [ DNSType : : SOA ] ; // fetch the SOA record to indicate NXDOMAIN ttl
2018-04-13 11:02:59 +02:00
response . putRR ( DNSSection : : Authority , zonename , DNSType : : SOA , rrset . ttl , rrset . contents [ 0 ] ) ;
}
else {
2018-04-17 12:32:56 +02:00
cout < < " \t Found node in zone ' " < < zonename < < " ' for lhs ' " < < qname < < " ', searchname now ' " < < searchname < < " ', lastnode ' " < < lastnode < < " ', passedZonecut= " < < passedZonecut < < endl ;
2018-04-13 11:02:59 +02:00
2018-04-13 15:44:11 +02:00
decltype ( node - > rrsets ) : : const_iterator iter ;
2018-04-16 17:09:27 +02:00
2018-04-13 11:02:59 +02:00
vector < DNSName > additional ;
2018-04-16 17:09:27 +02:00
// first we always check for a CNAME, which should be the only RRType at a node if present
2018-04-13 11:02:59 +02:00
if ( iter = node - > rrsets . find ( DNSType : : CNAME ) , iter ! = node - > rrsets . end ( ) ) {
2018-04-17 12:32:56 +02:00
2018-04-13 11:02:59 +02:00
const auto & rrset = iter - > second ;
response . putRR ( DNSSection : : Answer , lastnode + zonename , DNSType : : CNAME , rrset . ttl , rrset . contents [ 0 ] ) ;
DNSName target = dynamic_cast < CNAMEGen * > ( rrset . contents [ 0 ] . get ( ) ) - > d_name ;
2018-04-12 16:16:31 +02:00
2018-04-16 17:09:27 +02:00
// we'll only follow in-zone CNAMEs, which is not quite per-RFC, but a good idea
2018-04-13 11:02:59 +02:00
if ( target . makeRelative ( zonename ) ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Found CNAME, chasing to " < < target < < endl ;
2018-04-13 11:02:59 +02:00
searchname = target ;
2018-04-13 17:39:44 +02:00
if ( qtype ! = DNSType : : CNAME & & CNAMELoopCount + + < 10 ) { // do not loop if they *wanted* the CNAME
2018-04-13 11:02:59 +02:00
lastnode . clear ( ) ;
zonecutname . clear ( ) ;
goto loopCNAME ;
2018-04-12 16:16:31 +02:00
}
}
2018-04-16 17:09:27 +02:00
} // we have a node, and it might even have RRSets we want
2018-04-13 11:02:59 +02:00
else if ( iter = node - > rrsets . find ( qtype ) , iter ! = node - > rrsets . end ( ) | | ( ! node - > rrsets . empty ( ) & & qtype = = DNSType : : ANY ) ) {
auto range = make_pair ( iter , iter ) ;
2018-04-16 17:09:27 +02:00
if ( qtype = = DNSType : : ANY ) // if ANY, loop over all types
2018-04-13 11:02:59 +02:00
range = make_pair ( node - > rrsets . begin ( ) , node - > rrsets . end ( ) ) ;
else
2018-04-16 17:09:27 +02:00
+ + range . second ; // only the qtype they wanted
2018-04-13 11:02:59 +02:00
for ( auto i2 = range . first ; i2 ! = range . second ; + + i2 ) {
const auto & rrset = i2 - > second ;
2018-04-12 16:16:31 +02:00
for ( const auto & rr : rrset . contents ) {
2018-04-13 11:02:59 +02:00
response . putRR ( DNSSection : : Answer , lastnode + zonename , i2 - > first , rrset . ttl , rr ) ;
if ( i2 - > first = = DNSType : : MX )
2018-04-12 16:16:31 +02:00
additional . push_back ( dynamic_cast < MXGen * > ( rr . get ( ) ) - > d_name ) ;
}
}
2018-04-11 22:12:57 +02:00
}
2018-04-13 11:02:59 +02:00
else {
2018-04-17 12:32:56 +02:00
cout < < " \t Node exists, qtype doesn't, NOERROR situation, inserting SOA " < < endl ;
2018-04-13 11:02:59 +02:00
const auto & rrset = bestzone - > rrsets [ DNSType : : SOA ] ;
2018-04-16 16:36:51 +02:00
response . putRR ( DNSSection : : Authority , zonename , DNSType : : SOA , rrset . ttl , rrset . contents [ 0 ] ) ;
2018-04-13 11:02:59 +02:00
}
2018-04-17 12:32:56 +02:00
addAdditional ( bestzone , zonename , additional , response ) ;
2018-04-11 22:12:57 +02:00
}
return true ;
2018-04-03 18:03:59 +02:00
}
2018-04-11 22:12:57 +02:00
catch ( std : : out_of_range & e ) { // exceeded packet size
2018-04-17 12:32:56 +02:00
cout < < " \t Query for ' " < < origname < < " '| " < < qtype < < " got truncated " < < endl ;
2018-04-13 15:23:00 +02:00
response . clearRRs ( ) ;
response . dh . aa = 0 ; response . dh . tc = 1 ;
2018-04-11 22:12:57 +02:00
return true ;
}
catch ( std : : exception & e ) {
2018-04-17 12:32:56 +02:00
cout < < " \t Error processing query: " < < e . what ( ) < < endl ;
2018-04-11 22:12:57 +02:00
return false ;
2018-04-03 18:03:59 +02:00
}
}
2018-04-01 18:31:41 +02:00
2018-04-16 17:09:27 +02:00
/* this is where all UDP questions come in. Note that 'zones' is const,
which protects us from accidentally changing anything */
2018-04-03 18:03:59 +02:00
void udpThread ( ComboAddress local , Socket * sock , const DNSNode * zones )
{
2018-04-16 17:09:27 +02:00
DNSName qname ;
DNSType qtype ;
2018-04-01 18:31:41 +02:00
for ( ; ; ) {
ComboAddress remote ( local ) ;
2018-04-13 13:37:17 +02:00
string message = SRecvfrom ( * sock , 512 , remote ) ;
DNSMessageReader dm ( message ) ;
2018-04-13 15:23:00 +02:00
dm . getQuestion ( qname , qtype ) ;
2018-04-16 17:09:27 +02:00
2018-04-13 15:23:00 +02:00
DNSMessageWriter response ( qname , qtype ) ;
2018-04-16 17:09:27 +02:00
if ( processQuestion ( * zones , dm , remote , response ) ) {
if ( response . dh . rcode )
cout < < " Sending response with rcode " < < ( RCode ) response . dh . rcode < < endl ;
SSendto ( * sock , response . serialize ( ) , remote ) ;
2018-04-03 18:03:59 +02:00
}
}
}
2018-04-01 18:31:41 +02:00
2018-04-17 12:32:56 +02:00
/** \brief Looks up additional records
This function is called to do additional processing on records we encountered
earlier that would benefit . This includes MX and NS records .
Note that this function will only ook within ' bestzone ' , the best zone we had
for the original query . This means we will not look at potentially helpful
records in other zones . RFCs tell us that resolvers should not use / trust such
out of zone data anyhow , but no RFC tells us we should not add that data .
But we don ' t */
void addAdditional ( const DNSNode * bestzone , const DNSName & zone , const vector < DNSName > & toresolve , DNSMessageWriter & response )
try
{
for ( auto addname : toresolve ) {
if ( ! addname . makeRelative ( zone ) ) {
// cout<<addname<<" is not within our zone, not doing glue"<<endl;
continue ;
}
DNSName wuh ;
auto addnode = bestzone - > find ( addname , wuh ) ;
if ( ! addnode | | ! addname . empty ( ) ) {
continue ;
}
for ( auto & type : { DNSType : : A , DNSType : : AAAA } ) {
auto iter2 = addnode - > rrsets . find ( type ) ;
if ( iter2 ! = addnode - > rrsets . end ( ) ) {
const auto & rrset = iter2 - > second ;
for ( const auto & rr : rrset . contents ) {
response . putRR ( DNSSection : : Additional , wuh + zone , type , rrset . ttl , rr ) ;
}
}
}
}
}
catch ( std : : out_of_range & e ) { // exceeded packet size
cout < < " \t Additional records would have overflowed the packet, stopped adding them, not truncating yet \n " ;
}
2018-04-17 14:19:44 +02:00
/*! \brief Writes a DNSMessageWriter to a TCP/IP socket, with length envelope
helper function which encapsulates a DNS message within an ' envelope '
2018-04-16 17:09:27 +02:00
Note that it is highly recommended to send the envelope ( with length )
as a single call . This saves packets and works around implementation bugs
over at resolvers */
2018-04-16 00:14:18 +02:00
static void writeTCPMessage ( int sock , DNSMessageWriter & response )
2018-04-08 23:20:11 +02:00
{
string ser = " 00 " + response . serialize ( ) ;
uint16_t len = htons ( ser . length ( ) - 2 ) ;
ser [ 0 ] = * ( ( char * ) & len ) ;
ser [ 1 ] = * ( ( ( char * ) & len ) + 1 ) ;
2018-04-16 17:09:27 +02:00
SWriten ( sock , ser ) ;
2018-04-08 23:20:11 +02:00
}
2018-04-17 14:19:44 +02:00
/*! helper to read a 16 bit length in network order. Returns 0 on EOF */
2018-04-16 00:14:18 +02:00
uint16_t tcpGetLen ( int sock )
{
string message = SRead ( sock , 2 ) ;
if ( message . empty ( ) )
return 0 ;
if ( message . size ( ) ! = 2 ) {
throw std : : runtime_error ( " Incomplete TCP/IP message " ) ;
}
uint16_t len ;
memcpy ( & len , & message . at ( 1 ) - 1 , 2 ) ;
return htons ( len ) ;
}
2018-04-17 14:19:44 +02:00
/*! spawned for each new TCP/IP client. In actual production this is not a good idea. */
2018-04-16 17:09:27 +02:00
void tcpClientThread ( ComboAddress remote , int s , const DNSNode * zones )
2018-04-03 18:03:59 +02:00
{
2018-04-16 17:09:27 +02:00
signal ( SIGPIPE , SIG_IGN ) ;
Socket sock ( s ) ; // this will close for us
2018-04-03 18:03:59 +02:00
cout < < " TCP Connection from " < < remote . toStringWithPort ( ) < < endl ;
2018-04-16 17:09:27 +02:00
// multiple questions can come in over a single TCP/IP connection
2018-04-03 18:03:59 +02:00
for ( ; ; ) {
2018-04-16 00:14:18 +02:00
uint16_t len = tcpGetLen ( sock ) ;
2018-04-16 17:09:27 +02:00
if ( ! len ) // likely EOF
2018-04-16 00:14:18 +02:00
return ;
2018-04-03 18:03:59 +02:00
if ( len > 512 ) {
cerr < < " Remote " < < remote . toStringWithPort ( ) < < " sent question that was too big " < < endl ;
return ;
2018-04-03 13:18:37 +02:00
}
2018-04-03 18:03:59 +02:00
if ( len < sizeof ( dnsheader ) ) {
cerr < < " Dropping query from " < < remote . toStringWithPort ( ) < < " , too short " < < endl ;
return ;
2018-04-03 13:18:37 +02:00
}
2018-04-03 18:03:59 +02:00
2018-04-16 00:14:18 +02:00
std : : string message = SRead ( sock , len ) ;
2018-04-13 13:37:17 +02:00
DNSMessageReader dm ( message ) ;
2018-04-03 18:03:59 +02:00
2018-04-12 16:31:43 +02:00
DNSName name ;
2018-04-03 18:03:59 +02:00
DNSType type ;
dm . getQuestion ( name , type ) ;
2018-04-14 20:16:56 +02:00
2018-04-16 00:14:18 +02:00
DNSMessageWriter response ( name , type , 16384 ) ;
2018-04-16 17:09:27 +02:00
2018-04-14 20:16:56 +02:00
if ( type = = DNSType : : AXFR | | type = = DNSType : : IXFR ) {
if ( dm . dh . opcode | | dm . dh . qr ) {
cerr < < " Dropping non-query AXFR from " < < remote . toStringWithPort ( ) < < endl ; // too weird
return ;
}
cout < < " AXFR requested for " < < name < < endl ;
2018-04-08 23:20:11 +02:00
2018-04-13 15:23:00 +02:00
response . dh . id = dm . dh . id ;
2018-04-13 13:37:17 +02:00
response . dh . ad = response . dh . ra = response . dh . aa = 0 ;
response . dh . qr = 1 ;
2018-04-12 16:31:43 +02:00
DNSName zone ;
2018-04-16 17:09:27 +02:00
// as in processQuestion, find the best zone
2018-04-08 23:20:11 +02:00
auto fnd = zones - > find ( name , zone ) ;
2018-04-09 23:04:13 +02:00
if ( ! fnd | | ! fnd - > zone | | ! name . empty ( ) | | ! fnd - > zone - > rrsets . count ( DNSType : : SOA ) ) {
cout < < " This was not a zone, or zone had no SOA " < < endl ;
2018-04-13 13:37:17 +02:00
response . dh . rcode = ( int ) RCode : : Refused ;
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-13 13:37:17 +02:00
continue ;
2018-04-08 23:20:11 +02:00
}
2018-04-16 00:14:18 +02:00
auto node = fnd - > zone . get ( ) ;
2018-04-08 23:20:11 +02:00
2018-04-16 17:09:27 +02:00
// send SOA, which is how an AXFR must start
2018-04-08 23:20:11 +02:00
response . putRR ( DNSSection : : Answer , zone , DNSType : : SOA , node - > rrsets [ DNSType : : SOA ] . ttl , node - > rrsets [ DNSType : : SOA ] . contents [ 0 ] ) ;
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-13 15:23:00 +02:00
response . clearRRs ( ) ;
2018-04-08 23:20:11 +02:00
// send all other records
2018-04-12 16:31:43 +02:00
node - > visit ( [ & response , & sock , & name , & type , & zone ] ( const DNSName & nname , const DNSNode * n ) {
2018-04-08 23:20:11 +02:00
for ( const auto & p : n - > rrsets ) {
2018-04-16 17:09:27 +02:00
if ( p . first = = DNSType : : SOA ) // skip the SOA, as it indicates end of AXFR
2018-04-08 23:20:11 +02:00
continue ;
for ( const auto & rr : p . second . contents ) {
retry :
try {
response . putRR ( DNSSection : : Answer , nname , p . first , p . second . ttl , rr ) ;
}
2018-04-11 22:12:57 +02:00
catch ( std : : out_of_range & e ) { // exceeded packet size
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-13 15:23:00 +02:00
response . clearRRs ( ) ;
2018-04-08 23:20:11 +02:00
goto retry ;
}
}
}
} , zone ) ;
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-13 15:23:00 +02:00
response . clearRRs ( ) ;
2018-04-08 23:20:11 +02:00
// send SOA again
response . putRR ( DNSSection : : Answer , zone , DNSType : : SOA , node - > rrsets [ DNSType : : SOA ] . ttl , node - > rrsets [ DNSType : : SOA ] . contents [ 0 ] ) ;
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-03 18:03:59 +02:00
return ;
2018-04-01 18:31:41 +02:00
}
else {
2018-04-16 17:09:27 +02:00
if ( processQuestion ( * zones , dm , remote , response ) ) {
2018-04-16 00:14:18 +02:00
writeTCPMessage ( sock , response ) ;
2018-04-03 18:03:59 +02:00
}
else
return ;
2018-04-01 18:31:41 +02:00
}
}
2018-04-03 13:18:37 +02:00
}
2018-04-17 14:19:44 +02:00
//! connects to an authoritative server, retrieves a zone, returns it as a smart pointer
2018-04-16 00:14:18 +02:00
std : : unique_ptr < DNSNode > retrieveZone ( const ComboAddress & remote , const DNSName & zone )
{
cout < < " Attempting to retrieve zone " < < zone < < " from " < < remote . toStringWithPort ( ) < < endl ;
Socket tcp ( remote . sin4 . sin_family , SOCK_STREAM ) ;
2018-04-16 17:09:27 +02:00
2018-04-16 00:14:18 +02:00
SConnect ( tcp , remote ) ;
DNSMessageWriter dmw ( zone , DNSType : : AXFR ) ;
writeTCPMessage ( tcp , dmw ) ;
auto ret = std : : make_unique < DNSNode > ( ) ;
int soaCount = 0 ;
2018-04-17 12:32:56 +02:00
uint32_t rrcount = 0 ;
2018-04-16 00:14:18 +02:00
for ( ; ; ) {
uint16_t len = tcpGetLen ( tcp ) ;
string message = SRead ( tcp , len ) ;
DNSMessageReader dmr ( message ) ;
if ( dmr . dh . rcode ! = ( int ) RCode : : Noerror ) {
cout < < " Got error " < < dmr . dh . rcode < < " from auth " < < remote . toStringWithPort ( ) < < " when attempting to retrieve " < < zone < < endl ;
return std : : unique_ptr < DNSNode > ( ) ;
}
DNSName rrname ;
DNSType rrtype ;
DNSSection rrsection ;
uint32_t ttl ;
std : : unique_ptr < RRGen > rr ;
2018-04-17 12:32:56 +02:00
2018-04-16 00:14:18 +02:00
while ( dmr . getRR ( rrsection , rrname , rrtype , ttl , rr ) ) {
2018-04-17 12:32:56 +02:00
+ + rrcount ;
2018-04-16 00:14:18 +02:00
if ( ! rrname . makeRelative ( zone ) )
continue ;
if ( rrtype = = DNSType : : SOA & & + + soaCount = = 2 )
goto done ;
2018-04-16 17:09:27 +02:00
2018-04-16 00:14:18 +02:00
ret - > add ( rrname ) - > addRRs ( std : : move ( rr ) ) ;
ret - > add ( rrname ) - > rrsets [ rrtype ] . ttl = ttl ;
}
}
done :
2018-04-17 12:32:56 +02:00
cout < < " Done with AXFR of " < < zone < < " from " < < remote . toStringWithPort ( ) < < " , retrieved " < < rrcount < < " records " < < endl ;
2018-04-16 00:14:18 +02:00
return ret ;
}
2018-04-17 14:19:44 +02:00
//! This is the main tdns function
2018-04-03 13:18:37 +02:00
int main ( int argc , char * * argv )
2018-04-09 23:04:13 +02:00
try
2018-04-03 13:18:37 +02:00
{
2018-04-09 23:04:13 +02:00
if ( argc ! = 2 ) {
cerr < < " Syntax: tdns ipaddress:port " < < endl ;
return ( EXIT_FAILURE ) ;
}
2018-04-17 12:32:56 +02:00
cout < < " Hello and welcome to tdns, the teaching authoritative nameserver " < < endl ;
2018-04-09 12:54:27 +02:00
signal ( SIGPIPE , SIG_IGN ) ;
2018-04-17 12:32:56 +02:00
2018-04-03 13:18:37 +02:00
ComboAddress local ( argv [ 1 ] , 53 ) ;
2018-04-03 18:03:59 +02:00
Socket udplistener ( local . sin4 . sin_family , SOCK_DGRAM ) ;
SBind ( udplistener , local ) ;
2018-04-17 12:32:56 +02:00
2018-04-03 18:03:59 +02:00
Socket tcplistener ( local . sin4 . sin_family , SOCK_STREAM ) ;
SSetsockopt ( tcplistener , SOL_SOCKET , SO_REUSEPORT , 1 ) ;
SBind ( tcplistener , local ) ;
SListen ( tcplistener , 10 ) ;
2018-04-16 17:09:27 +02:00
2018-04-17 12:32:56 +02:00
DNSNode zones ;
cout < < " Loading & retrieving zone data " < < endl ;
2018-04-03 13:18:37 +02:00
loadZones ( zones ) ;
2018-04-16 00:14:18 +02:00
2018-04-17 12:32:56 +02:00
cout < < " Listening on TCP & UDP on " < < local . toStringWithPort ( ) < < endl ;
2018-04-03 13:18:37 +02:00
2018-04-17 12:32:56 +02:00
thread udpServer ( udpThread , local , & udplistener , & zones ) ;
cout < < " Server is live " < < endl ;
2018-04-03 18:03:59 +02:00
for ( ; ; ) {
2018-04-16 17:09:27 +02:00
ComboAddress remote ( local ) ; // this sets the family correctly
2018-04-03 18:03:59 +02:00
int client = SAccept ( tcplistener , remote ) ;
2018-04-16 17:09:27 +02:00
thread t ( tcpClientThread , remote , client , & zones ) ;
2018-04-03 18:03:59 +02:00
t . detach ( ) ;
}
2018-04-01 18:31:41 +02:00
}
2018-04-09 23:04:13 +02:00
catch ( std : : exception & e )
{
cerr < < " Fatal error: " < < e . what ( ) < < endl ;
return EXIT_FAILURE ;
}