Skip to content
Permalink
c14b5d4010
Go to file
 
 
Cannot retrieve contributors at this time
2301 lines (1833 sloc) 66.1 KB
/*------------------------------------------------------------------------------
*
* Copyright (c) 2011-2017, EURid. All rights reserved.
* The YADIFA TM software product is provided under the BSD 3-clause license:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of EURid nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*------------------------------------------------------------------------------
*
*/
/** @defgroup dnspacket DNS Messages
* @ingroup dnscore
* @brief
*
* @{
*/
/*------------------------------------------------------------------------------
*
* USE INCLUDES */
#include "dnscore/dnscore-config.h"
#include <unistd.h>
#include <stddef.h>
#include "dnscore/dnscore-config.h"
#include "dnscore/message.h"
#include "dnscore/logger.h"
#include "dnscore/dnscore.h"
#include "dnscore/format.h"
#include "dnscore/fingerprint.h"
#include "dnscore/packet_reader.h"
#include "dnscore/packet_writer.h"
#include "dnscore/tsig.h"
#include "dnscore/fdtools.h"
#include "dnscore/tcp_io_stream.h"
#include "dnscore/counter_output_stream.h"
#include "dnscore/network.h"
#include "dnscore/thread_pool.h"
#if HAS_CTRL
#include "dnscore/ctrl-rfc.h"
#endif
/*------------------------------------------------------------------------------
* GLOBAL VARIABLES */
extern logger_handle *g_system_logger;
#define MODULE_MSG_HANDLE g_system_logger
#define SA_LOOP 3
#define SA_PRINT 4
/*------------------------------------------------------------------------------
* FUNCTIONS */
u16 edns0_maxsize = EDNS0_MAX_LENGTH;
void message_edns0_setmaxsize(u16 maxsize)
{
edns0_maxsize = maxsize;
}
u16 message_edns0_getmaxsize()
{
return edns0_maxsize;
}
// Handles OPT and TSIG
static ya_result
message_process_additionals(message_data *mesg, u8* s, u16 ar_count)
{
/*
* @note: I've moved this in the main function (the one calling this one)
*/
//yassert(ar_count != 0 && ar_count == MESSAGE_AR(mesg->buffer));
u8 *buffer = mesg->buffer;
ar_count = ntohs(MESSAGE_AR(buffer));
/*
* rfc2845
*
* If there is a TSIG then
* _ It must be put aside, safely
* _ It must be removed from the query
* _ It must be processed
*
* rfc2671
*
* Handle OPT
*
*/
/*
* Read DNS name (decompression on)
* Read type (TSIG = 250)
* Read class (ANY)
* Read TTL (0)
* Read RDLEN
*
*/
u32 query_end = mesg->received;
packet_unpack_reader_data purd;
purd.packet = buffer;
purd.packet_size = mesg->received;
if(mesg->ar_start == NULL)
{
u32 ar_index = ntohs(MESSAGE_AN(buffer)) + ntohs(MESSAGE_NS(buffer));
purd.offset = DNS_HEADER_LENGTH; /* Header */
packet_reader_skip_fqdn(&purd); /* Query DNAME */
purd.offset += 4; /* TYPE CLASS */
while(ar_index > 0) /* Skip all until AR records */
{
/*
* It should be in this kind of processing that we read the EDNS0 flag
*/
packet_reader_skip_record(&purd);
ar_index--;
}
query_end = purd.offset;
mesg->ar_start = &mesg->buffer[purd.offset];
}
else
{
purd.offset = mesg->ar_start - mesg->buffer;
}
/* We are now at the start of the ar */
struct type_class_ttl_rdlen tctr;
u8 tsigname[MAX_DOMAIN_LENGTH];
#if DNSCORE_HAS_TSIG_SUPPORT
u32 record_offset;
#endif
while(ar_count-- > 0)
{
#if DNSCORE_HAS_TSIG_SUPPORT
record_offset = purd.offset;
#endif
if(FAIL(packet_reader_read_fqdn(&purd, tsigname, sizeof(tsigname))))
{
/* oops */
mesg->status = FP_ERROR_READING_QUERY;
return UNPROCESSABLE_MESSAGE;
}
if(packet_reader_read(&purd, &tctr, 10) == 10 )
{
/*
* EDNS (0)
*/
if(tctr.qtype == TYPE_OPT)
{
/**
* Handle EDNS
*
* @todo 20140523 edf -- improve the EDNS handling
* @todo 20140523 edf -- handle extended RCODE (supposed to be 0, but could be set to something else : FORMERR)
*/
if((tctr.ttl & NU32(0x00ff0000)) == 0) /* ensure version is 0 */
{
u32 rdlen = ntohs(tctr.rdlen);
#if DNSCORE_HAS_NSID_SUPPORT
if(rdlen != 0)
{
u32 next = purd.offset + rdlen;
for(u32 remain = rdlen; remain >= 4; remain -= 4)
{
u32 opt_type_size;
packet_reader_read_u32(&purd, &opt_type_size);
if(opt_type_size == NU32(0x00030000))
{
// nsid
mesg->nsid = TRUE;
break;
}
packet_reader_skip(&purd, ntohl(opt_type_size) & 0xffff);
}
packet_reader_skip(&purd, next - purd.offset);
}
#else
packet_reader_skip(&purd, rdlen);
#endif
if(tsigname[0] == '\0')
{
mesg->size_limit = MAX(EDNS0_MIN_LENGTH, ntohs(tctr.qclass)); /* our own limit, taken from the config file */
mesg->edns = TRUE;
mesg->rcode_ext = tctr.ttl;
log_debug("EDNS: udp-size=%d rcode-ext=%08x desc=%04x", mesg->size_limit, tctr.ttl, rdlen);
continue;
}
}
else
{
mesg->status = FP_EDNS_BAD_VERSION;
mesg->size_limit = MAX(EDNS0_MIN_LENGTH, ntohs(tctr.qclass));
mesg->edns = TRUE;
mesg->rcode_ext = 0;
}
log_debug("OPT record is not processable (broken or not supported)");
return UNPROCESSABLE_MESSAGE;
}
#if DNSCORE_HAS_TSIG_SUPPORT
/*
* TSIG
*/
else if(tctr.qtype == TYPE_TSIG)
{
if(ar_count == 0)
{
/*
* It looks like a TSIG ...
*/
ya_result return_code;
if(message_isquery(mesg))
{
if(FAIL(return_code = tsig_process_query(mesg, &purd, record_offset, tsigname, &tctr)))
{
log_err("%r query error from %{sockaddr}", return_code, &mesg->other.sa);
return UNPROCESSABLE_MESSAGE;
}
}
else
{
tsig_item *key = tsig_get(tsigname);
if(key != NULL)
{
if(FAIL(return_code = tsig_process(mesg, &purd, record_offset, key, &tctr)))
{
log_err("%r answer error from %{sockaddr}", return_code, &mesg->other.sa);
return UNPROCESSABLE_MESSAGE;
}
}
else
{
log_err("answer error from %{sockaddr}: TSIG when none expected", &mesg->other.sa);
mesg->status = FP_TSIG_UNEXPECTED;
return INVALID_MESSAGE;
}
}
break; /* we know there is no need to loop anymore */
}
else
{
/*
* Error: TSIG is not the last AR record
*/
#if DEBUG
log_debug("TSIG record is not the last AR");
#endif
mesg->status = FP_TSIG_IS_NOT_LAST;
return UNPROCESSABLE_MESSAGE;
}
}
#endif
else
{
/* Unhandled AR TYPE */
log_debug("Unhandled AR type %{dnstype}", &tctr.qtype);
mesg->status = FP_UNEXPECTED_RR_IN_QUERY;
return UNPROCESSABLE_MESSAGE;
}
}
} /* While there are AR to process */
mesg->received = query_end;
return SUCCESS;
}
static ya_result
message_process_answer_additionals(message_data *mesg, u8* s, u16 ar_count)
{
/*
* @note: I've moved this in the main function (the one calling this one)
*/
yassert(ar_count != 0 && ar_count == MESSAGE_AR(mesg->buffer));
u8 *buffer = mesg->buffer;
ar_count = ntohs(ar_count);
/*
* rfc2845
*
* If there is a TSIG then
* _ It must be put aside, safely
* _ It must be removed from the query
* _ It must be processed
*
* rfc2671
*
* Handle OPT
*
*/
/*
* Read DNS name (decompression on)
* Read type (TSIG = 250)
* Read class (ANY)
* Read TTL (0)
* Read RDLEN
*
*/
u32 query_end = mesg->received;
packet_unpack_reader_data purd;
purd.packet = buffer;
purd.packet_size = mesg->received;
if(mesg->ar_start == NULL)
{
u32 ar_index = ntohs(MESSAGE_AN(buffer)) + ntohs(MESSAGE_NS(buffer));
purd.offset = DNS_HEADER_LENGTH; /* Header */
packet_reader_skip_fqdn(&purd); /* Query DNAME */
purd.offset += 4; /* TYPE CLASS */
while(ar_index > 0) /* Skip all until AR records */
{
/*
* It should be in this kind of processing that we read the EDNS0 flag
*/
packet_reader_skip_record(&purd);
ar_index--;
}
query_end = purd.offset;
mesg->ar_start = &mesg->buffer[purd.offset];
}
else
{
purd.offset = mesg->ar_start - mesg->buffer;
}
/* We are now at the start of the ar */
struct type_class_ttl_rdlen tctr;
u8 tsigname[MAX_DOMAIN_LENGTH];
#if DNSCORE_HAS_TSIG_SUPPORT
u32 record_offset;
#endif
while(ar_count-- > 0)
{
#if DNSCORE_HAS_TSIG_SUPPORT
record_offset = purd.offset;
#endif
if(FAIL(packet_reader_read_fqdn(&purd, tsigname, sizeof(tsigname))))
{
/* oops */
mesg->status = FP_ERROR_READING_QUERY;
return UNPROCESSABLE_MESSAGE;
}
if(packet_reader_read(&purd, &tctr, 10) == 10 )
{
/*
* EDNS (0)
*/
if(tctr.qtype == TYPE_OPT)
{
/**
* Handle EDNS
*
* @todo 20140526 edf -- improve the EDNS handling
* @todo 20140526 edf -- handle extended RCODE (supposed to be 0, but could be set to something else : FORMERR)
*/
MESSAGE_SET_AR(mesg->buffer,htons(ntohs(MESSAGE_AR(mesg->buffer)) - 1));
if((tctr.ttl & NU32(0x00ff0000)) == 0) /* ensure version is 0 */
{
if(tsigname[0] == '\0')
{
mesg->size_limit = edns0_maxsize; /* our own limit, taken from the config file */
mesg->edns = TRUE;
mesg->rcode_ext = tctr.ttl;
log_debug("EDNS: udp-size=%d rcode-ext=%08x desc=%04x", mesg->size_limit, tctr.ttl, ntohs(tctr.rdlen));
continue;
}
}
else
{
mesg->status = FP_EDNS_BAD_VERSION;
mesg->size_limit = edns0_maxsize;
mesg->edns = TRUE;
mesg->rcode_ext = 0;
/* we are after tctr in the packet : rewind by 6 */
/*
MESSAGE_FLAGS_OR(buffer, QR_BITS, FP_EDNS_BAD_VERSION & 15);
buffer[purd.offset - 6] = (FP_EDNS_BAD_VERSION >> 4);
buffer[purd.offset - 5] = 0;
*/
}
log_debug("OPT record is not processable (broken or not supported)");
return UNPROCESSABLE_MESSAGE;
}
#if DNSCORE_HAS_TSIG_SUPPORT
/*
* TSIG
*/
else if(tctr.qtype == TYPE_TSIG)
{
if(ar_count == 0)
{
/*
* It looks like a TSIG ...
*/
ya_result return_code;
if(message_isquery(mesg))
{
if(FAIL(return_code = tsig_process_query(mesg, &purd, record_offset, tsigname, &tctr)))
{
log_err("%r query error from %{sockaddr}", return_code, &mesg->other.sa);
switch(return_code)
{
case TSIG_BADKEY:
case TSIG_BADTIME:
case TSIG_BADSIG:
/* There is a TSIG and it's bad : NOTAUTH
*
* The query TSIG has been removed already.
* A new TSIG with no-mac one with the return_code set in the error field must be added to the result.
*/
tsig_append_error(mesg);
break;
default:
/*
* discard : FORMERR
*/
break;
}
return UNPROCESSABLE_MESSAGE;
}
}
else // not a query (an answer)
{
u8 old_mac[64];
if(mesg->tsig.tsig != NULL)
{
if(dnsname_equals(tsigname, mesg->tsig.tsig->name))
{
u16 old_mac_size = mesg->tsig.mac_size;
memcpy(old_mac, mesg->tsig.mac, old_mac_size);
if(FAIL(return_code = tsig_process_answer(mesg, &purd, record_offset, mesg->tsig.tsig, &tctr, old_mac, old_mac_size)))
{
log_err("%r answer error from %{sockaddr}", return_code, &mesg->other.sa);
return UNPROCESSABLE_MESSAGE;
}
}
}
else // no tsig
{
log_err("answer error from %{sockaddr}: TSIG when none expected", &mesg->other.sa);
mesg->status = FP_TSIG_UNEXPECTED;
return UNPROCESSABLE_MESSAGE;
}
}
break; /* we know there is no need to loop anymore */
}
else
{
/*
* Error: TSIG is not the last AR record
*/
log_debug("TSIG record is not the last AR");
mesg->status = FP_TSIG_IS_NOT_LAST;
return UNPROCESSABLE_MESSAGE;
}
}
#endif
else
{
/* Unhandled AR TYPE */
#ifdef DEBUG
log_debug("skipping AR type %{dnstype}", &tctr.qtype);
#endif
purd.offset += ntohs(tctr.rdlen);
query_end = purd.offset;
}
}
} /* While there are AR to process */
mesg->received = query_end;
return SUCCESS;
}
/** \brief Processing DNS packet
*
* @param mesg
*
* @retval OK
* @return status of message is written in mesg->status
*/
/* Defines a mask and the expected result for the 4 first 16 bits of the header */
#ifdef WORDS_BIGENDIAN
#define MESSAGE_HEADER_MASK (( (u64) 0 ) | \
( ((u64) ( QR_BITS | AA_BITS | RA_BITS | TC_BITS )) << 40 ) | \
( ((u64) ( RA_BITS | RCODE_BITS )) << 32 ) | \
( ((u64) 1LL) << 16 ))
#define MESSAGE_HEADER_RESULT ( ((u64) 1LL) << 16 )
/* Bind gives "RA" here (seems irrelevant, nonsense, but we need to accept it) */
#define NOTIFY_MESSAGE_HEADER_MASK (( (u64) 0LL ) | \
( ((u64) ( TC_BITS )) << 40 ) | \
( ((u64) 1LL) << 16 ))
#define NOTIFY_MESSAGE_HEADER_RESULT ( ((u64) 1LL) << 16 )
#else
#define MESSAGE_HEADER_MASK (( (u64) 0LL ) | \
( ((u64) ( QR_BITS | AA_BITS | RA_BITS | TC_BITS )) << 16 ) | \
( ((u64) ( RA_BITS | RCODE_BITS )) << 24 ) | \
( ((u64) 1LL) << 40 ))
#define MESSAGE_HEADER_RESULT ( ((u64) 1LL) << 40 )
/* Bind gives "RA" here (seems irrelevant, nonsense, but we need to accept it) */
#define NOTIFY_MESSAGE_HEADER_MASK (( (u64) 0LL ) | \
( ((u64) ( TC_BITS )) << 16 ) | \
( ((u64) 1LL) << 40 ))
#define NOTIFY_MESSAGE_HEADER_RESULT ( ((u64) 1LL) << 40 )
#endif
/* EDF: this takes about 150 cycles [144;152] with peaks at 152 */
static inline u8*
message_process_copy_fqdn(message_data *mesg)
{
u8 *src = &mesg->buffer[DNS_HEADER_LENGTH];
u8 *dst = &mesg->qname[0];
u8 *base = dst;
u32 len;
for(;;)
{
len = *src++;
*dst++ = len;
if(len == 0)
{
break;
}
if( (len & 0xC0) == 0 )
{
const u8 * const limit = dst + len;
if(limit - base < MAX_DOMAIN_LENGTH)
{
do
{
*dst++ = LOCASE(*src++); /* Works with the dns character set */
}
while(dst < limit);
}
else
{
mesg->status = FP_NAME_TOO_LARGE;
//mesg->send_length = mesg->received;
DERROR_MSG("FP_NAME_TOO_LARGE");
return NULL;
}
}
else
{
/*
if((len & 0xC0) == 0xC0)
{
len &= 0x3f;
len <<= 8;
len |= *src;
src = &mesg->buffer[len];
}
else
{
mesg->status = FP_NAME_FORMAT_ERROR;
return NULL;
}
*/
mesg->status = ((len & 0xC0)==0xC0)?FP_QNAME_COMPRESSED:FP_NAME_FORMAT_ERROR;
return NULL;
}
}
/* Get qtype & qclass */
mesg->qtype = GET_U16_AT(src[0]); /** @note : NATIVETYPE */
mesg->qclass = GET_U16_AT(src[2]); /** @note : NATIVECLASS */
return src + 4;
}
ya_result
message_process_query(message_data *mesg)
{
u8 *buffer = mesg->buffer;
/** CHECK DNS HEADER */
/** Drop dns packet if query is answer or does not have correct header length */
/*
* +5 <=> 1 qd record ar least
*/
u64 *h64 = (u64*)buffer;
u64 m64 = MESSAGE_HEADER_MASK;
u64 r64 = MESSAGE_HEADER_RESULT;
if( (mesg->received < DNS_HEADER_LENGTH + 5) ||
(( *h64 & m64) != r64 ) )
{
/** Return if QDCOUNT is not 1
*
* @note Previous test was actually testing if QDCOUNT was > 1
* I assumed either 0 or >1 is wrong for us so I used the same trick than for QCCOUNT
*/
if(MESSAGE_QR(buffer))
{
mesg->status = FP_QR_BIT_SET;
return INVALID_MESSAGE;
}
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS|RD_BITS, 0);
if(NETWORK_ONE_16 != MESSAGE_QD(buffer))
{
if(0 == MESSAGE_QD(buffer))
{
DERROR_MSG("FP_QDCOUNT_IS_0");
mesg->status = FP_QDCOUNT_IS_0;
return INVALID_MESSAGE; /* will be dropped */
}
else
{
DERROR_MSG("FP_QDCOUNT_BIG_1");
mesg->status = FP_QDCOUNT_BIG_1;
}
}
else if(MESSAGE_NS(buffer) != 0)
{
mesg->status = FP_NSCOUNT_NOT_0;
}
else
{
mesg->status = FP_PACKET_DROPPED;
}
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
mesg->status = FP_NAME_FORMAT_ERROR;
return UNPROCESSABLE_MESSAGE;
}
/*
* Handle the OPT and TSIG records
*/
{
ya_result return_code;
u32 nsar_count;
if((nsar_count = MESSAGE_NSAR(buffer)) != 0)
{
if(FAIL(return_code = message_process_additionals(mesg, s, nsar_count)))
{
mesg->received = s - buffer;
return return_code;
}
}
if(mesg->qtype != TYPE_IXFR)
{
mesg->received = s - buffer;
}
}
/* cut the trash here */
/* At this point the TSIG has been computed and removed */
/* Clear zome bits */
MESSAGE_FLAGS_AND(mesg->buffer, ~(QR_BITS|TC_BITS|AA_BITS), ~(Z_BITS|AD_BITS|CD_BITS|RA_BITS|RCODE_BITS));
//MESSAGE_LOFLAGS(buffer) &= ~(Z_BITS|AD_BITS|CD_BITS|RCODE_BITS);
mesg->status = FP_MESG_OK;
return OK;
}
int
message_process(message_data *mesg)
{
u8 *buffer = mesg->buffer;
switch(MESSAGE_OP(buffer))
{
case OPCODE_QUERY:
{
/** CHECK DNS HEADER */
/** Drop dns packet if query is answer or does not have correct header length */
/*
* +5 <=> 1 qd record ar least
*/
u64 *h64 = (u64*)buffer;
u64 m64 = MESSAGE_HEADER_MASK;
u64 r64 = MESSAGE_HEADER_RESULT;
if( (mesg->received < DNS_HEADER_LENGTH + 5) ||
(( *h64 & m64) != r64 ) )
{
/** Return if QDCOUNT is not 1
*
* @note Previous test was actually testing if QDCOUNT was > 1
* I assumed either 0 or >1 is wrong for us so I used the same trick than for QCCOUNT
*/
if(MESSAGE_QR(buffer))
{
mesg->status = FP_QR_BIT_SET;
return INVALID_MESSAGE;
}
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS|RD_BITS, 0);
if(NETWORK_ONE_16 != MESSAGE_QD(buffer))
{
if(0 == MESSAGE_QD(buffer))
{
DERROR_MSG("FP_QDCOUNT_IS_0");
mesg->status = FP_QDCOUNT_IS_0;
return INVALID_MESSAGE; /* will be dropped */
}
else
{
mesg->status = FP_QDCOUNT_BIG_1;
DERROR_MSG("FP_QDCOUNT_BIG_1");
}
}
else if( MESSAGE_NS(buffer) != 0)
{
mesg->status = FP_NSCOUNT_NOT_0;
}
else
{
mesg->status = FP_PACKET_DROPPED;
}
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
mesg->status = FP_NAME_FORMAT_ERROR;
return UNPROCESSABLE_MESSAGE;
}
/*
* Handle the OPT and TSIG records
*/
{
ya_result return_code;
u32 nsar_count;
if((nsar_count = MESSAGE_NSAR(buffer)) != 0)
{
if(FAIL(return_code = message_process_additionals(mesg, s, nsar_count)))
{
mesg->received = s - buffer;
return return_code;
}
}
if(mesg->qtype != TYPE_IXFR)
{
mesg->received = s - buffer;
}
}
/* At this point the TSIG has been computed and removed */
/* Clear zome bits */
MESSAGE_FLAGS_AND(mesg->buffer, ~(QR_BITS|TC_BITS|AA_BITS), ~(Z_BITS|RA_BITS|AD_BITS|CD_BITS|RCODE_BITS));
mesg->status = FP_MESG_OK;
return OK;
}
case OPCODE_NOTIFY:
{
MESSAGE_LOFLAGS(buffer) &= ~(Z_BITS|AD_BITS|CD_BITS);
/* ------------------------------------------------------------ */
/** CHECK DNS HEADER */
/** Drop dns packet if query is answer or does not have correct header length */
/*
* +5 <=> 1 qd record ar least
*/
u64 *h64 = (u64*)buffer;
u64 m64 = NOTIFY_MESSAGE_HEADER_MASK;
u64 r64 = NOTIFY_MESSAGE_HEADER_RESULT;
/* ... A400 0001 ... */
if( (mesg->received < DNS_HEADER_LENGTH + 5) ||
(( *h64 & m64) != r64 ) )
{
/** Return if QDCOUNT is not 1
*
* @note Previous test was actually testing if QDCOUNT was > 1
* I assumed either 0 or >1 is wrong for us so I used the same trick than for QCCOUNT
*/
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS, 0);
if(NETWORK_ONE_16 != MESSAGE_QD(buffer))
{
if(0 == MESSAGE_QD(buffer))
{
DERROR_MSG("FP_QDCOUNT_IS_0");
mesg->status = FP_QDCOUNT_IS_0;
return INVALID_MESSAGE;
}
else
{
mesg->status = FP_QDCOUNT_BIG_1;
DERROR_MSG("FP_QDCOUNT_BIG_1");
}
}
else
{
mesg->status = FP_PACKET_DROPPED;
}
return UNPROCESSABLE_MESSAGE;
}
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
mesg->status = FP_NAME_FORMAT_ERROR;
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
/*
* If there is a TSIG, it is here ...
*/
#if DNSCORE_HAS_TSIG_SUPPORT
{
ya_result return_code;
u16 ar_count;
if((ar_count = MESSAGE_AR(buffer)) != 0)
{
if(FAIL(return_code = message_process_additionals(mesg, s, ar_count)))
{
return return_code;
}
}
}
#endif
/* At this point the TSIG has been computed and removed */
// MESSAGE_FLAGS_AND(mesg->buffer, ~(TC_BITS|AA_BITS), ~(RA_BITS|RCODE_BITS));
mesg->status = FP_MESG_OK;
return OK;
}
case OPCODE_UPDATE:
{
MESSAGE_LOFLAGS(buffer) &= ~(Z_BITS|AD_BITS|CD_BITS|RCODE_BITS);
/* ------------------------------------------------------------ */
/** CHECK DNS HEADER */
/** Drop dns packet if query is answer or does not have correct header length */
/*
* +5 <=> 1 qd record ar least
*/
u64 *h64 = (u64*)buffer;
u64 m64 = MESSAGE_HEADER_MASK;
u64 r64 = MESSAGE_HEADER_RESULT;
if( (mesg->received < DNS_HEADER_LENGTH + 5) ||
(( *h64 & m64) != r64 ) )
{
/** Return if QDCOUNT is not 1
*
* @note Previous test was actually testing if QDCOUNT was > 1
* I assumed either 0 or >1 is wrong for us so I used the same trick than for QCCOUNT
*/
if(MESSAGE_QR(buffer))
{
mesg->status = FP_QR_BIT_SET;
return INVALID_MESSAGE;
}
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS, 0);
if(NETWORK_ONE_16 != MESSAGE_QD(buffer))
{
if(0 == MESSAGE_QD(buffer))
{
DERROR_MSG("FP_QDCOUNT_IS_0");
mesg->status = FP_QDCOUNT_IS_0;
return INVALID_MESSAGE;
}
else
{
DERROR_MSG("FP_QDCOUNT_BIG_1");
mesg->status = FP_QDCOUNT_BIG_1;
}
return UNPROCESSABLE_MESSAGE;
}
mesg->status = FP_PACKET_DROPPED;
return UNPROCESSABLE_MESSAGE;
}
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
mesg->status = FP_NAME_FORMAT_ERROR;
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
/*
* If there is a TSIG, it is here ...
*/
#if DNSCORE_HAS_TSIG_SUPPORT
{
ya_result return_code;
u16 ar_count;
if((ar_count = MESSAGE_AR(buffer)) != 0)
{
if(FAIL(return_code = message_process_additionals(mesg, s, ar_count)))
{
return return_code;
}
}
}
#endif
/* At this point the TSIG has been computed and removed */
MESSAGE_FLAGS_AND(mesg->buffer, ~(QR_BITS|TC_BITS|AA_BITS), ~(RA_BITS|RCODE_BITS));
mesg->status = FP_MESG_OK;
return OK;
}
#if HAS_CTRL
case OPCODE_CTRL:
{
MESSAGE_LOFLAGS(buffer) &= ~(Z_BITS|AD_BITS|CD_BITS|RCODE_BITS);
/*
rdtsc_init(&mpb);
*/
/* ------------------------------------------------------------ */
/** CHECK DNS HEADER */
/** Drop dns packet if query is answer or does not have correct header length */
/*
* +5 <=> 1 qd record ar least
*/
u64 *h64 = (u64*)buffer;
u64 m64 = MESSAGE_HEADER_MASK;
u64 r64 = MESSAGE_HEADER_RESULT;
if( (mesg->received < DNS_HEADER_LENGTH + 5) ||
(( *h64 & m64) != r64 ) )
{
/** Return if QDCOUNT is not 1
*
* @note Previous test was actually testing if QDCOUNT was > 1
* I assumed either 0 or >1 is wrong for us so I used the same trick than for QCCOUNT
*/
if(MESSAGE_QR(buffer))
{
mesg->status = FP_QR_BIT_SET;
return INVALID_MESSAGE;
}
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS, 0);
if(NETWORK_ONE_16 != MESSAGE_QD(buffer))
{
if(0 == MESSAGE_QD(buffer))
{
DERROR_MSG("FP_QDCOUNT_IS_0");
mesg->status = FP_QDCOUNT_IS_0;
return INVALID_MESSAGE;
}
else
{
DERROR_MSG("FP_QDCOUNT_BIG_1");
mesg->status = FP_QDCOUNT_BIG_1;
}
return UNPROCESSABLE_MESSAGE;
}
mesg->status = FP_PACKET_DROPPED;
return UNPROCESSABLE_MESSAGE;
}
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
mesg->status = FP_NAME_FORMAT_ERROR;
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
/*
* If there is a TSIG, it is here ...
*/
#if DNSCORE_HAS_TSIG_SUPPORT
{
ya_result return_code;
u16 ar_count;
if((ar_count = MESSAGE_AR(buffer)) != 0)
{
if(FAIL(return_code = message_process_additionals(mesg, s, ar_count)))
{
return return_code;
}
}
}
#endif
/* At this point the TSIG has been computed and removed */
MESSAGE_FLAGS_AND(mesg->buffer, ~(QR_BITS|TC_BITS|AA_BITS), ~(RA_BITS|RCODE_BITS));
mesg->status = FP_MESG_OK;
return OK;
}
#endif // HAS_CTRL
default:
{
u8 hf = MESSAGE_HIFLAGS(buffer);
if((hf & QR_BITS) == 0)
{
MESSAGE_LOFLAGS(buffer) &= ~(Z_BITS|AD_BITS|CD_BITS|RCODE_BITS);
MESSAGE_FLAGS_AND(buffer, OPCODE_BITS, 0);
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
mesg->status = FP_NOT_SUPP_OPC;
mesg->received = DNS_HEADER_LENGTH;
SET_U32_AT(mesg->buffer[4],0); /* aligned to 32 bits, so two 32 bits instead of one 64 */
SET_U32_AT(mesg->buffer[8],0);
/* reserved for future use */
return UNPROCESSABLE_MESSAGE;
}
else
{
mesg->status = FP_PACKET_DROPPED;
return INVALID_MESSAGE;
}
}
}
}
int
message_process_lenient(message_data *mesg)
{
if(mesg->received < DNS_HEADER_LENGTH)
{
return UNPROCESSABLE_MESSAGE;
}
u8 *buffer = mesg->buffer;
u8 *s = message_process_copy_fqdn(mesg);
if(s == NULL)
{
return UNPROCESSABLE_MESSAGE;
}
/**
* @note Past this point, a message could be processable.
* It's the right place to reset the message's defaults.
*
*/
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = NULL;
mesg->rcode_ext = 0;
mesg->edns = FALSE;
#if DNSCORE_HAS_NSID_SUPPORT
mesg->nsid = FALSE;
#endif
/*
* Handle the OPT and TSIG records
*/
{
ya_result return_code;
u16 ar_count;
if((ar_count = MESSAGE_AR(buffer)) != 0)
{
if(FAIL(return_code = message_process_answer_additionals(mesg, s, ar_count)))
{
return return_code;
}
}
#if DNSCORE_HAS_TSIG_SUPPORT
else
{
mesg->tsig.tsig = NULL;
/* cut the trash here */
/*mesg->received = s - buffer;*/
}
#endif
}
/* At this point the TSIG has been computed and removed */
mesg->status = FP_MESG_OK;
return OK;
}
void
message_transform_to_error(message_data *mesg)
{
if(!mesg->edns)
{
MESSAGE_FLAGS_OR(mesg->buffer, QR_BITS, mesg->status);
if(mesg->status == RCODE_FORMERR)
{
SET_U32_AT(mesg->buffer[4],0); /* aligned to 32 bits, so two 32 bits instead of one 64 */
SET_U32_AT(mesg->buffer[8],0);
mesg->send_length = DNS_HEADER_LENGTH;
}
else
{
mesg->send_length = mesg->received;
}
}
else
{
/* 00 0029 0200 EE 00 00000000 */
if(mesg->status == RCODE_FORMERR)
{
SET_U32_AT(mesg->buffer[4],0); /* aligned to 32 bits, so two 32 bits instead of one 64 */
SET_U32_AT(mesg->buffer[8],0);
mesg->send_length = DNS_HEADER_LENGTH;
}
else
{
mesg->send_length = mesg->ar_start - mesg->buffer;
}
/* rcode */
//MESSAGE_LOFLAGS(mesg->buffer) &= RCODE_BITS;
MESSAGE_HIFLAGS(mesg->buffer) |= QR_BITS;
MESSAGE_LOFLAGS(mesg->buffer) |= mesg->status & 15;
/* #AR = 1 */
mesg->buffer[DNS_HEADER_LENGTH - 1] = 1; /* AR count was 0, now it is 1 */
/* append opt *//* */
u8 *buffer = &mesg->buffer[mesg->send_length];
buffer[ 0] = 0;
buffer[ 1] = 0;
buffer[ 2] = 0x29;
buffer[ 3] = edns0_maxsize>>8;
buffer[ 4] = edns0_maxsize;
buffer[ 5] = (mesg->status >> 4);
//buffer[ 6] = mesg->rcode_ext >> 24;
buffer[ 6] = mesg->rcode_ext >> 16;
buffer[ 7] = mesg->rcode_ext >> 8;
buffer[ 8] = mesg->rcode_ext;
buffer[ 9] = 0;
buffer[10] = 0;
mesg->send_length += 11;
}
}
void
message_make_query(message_data *mesg, u16 id, const u8 *qname, u16 qtype, u16 qclass)
{
#ifdef WORDS_BIGENDIAN
SET_U64_AT(mesg->buffer[0], 0x0000000000010000LL);
SET_U32_AT(mesg->buffer[8], 0);
#else
SET_U64_AT(mesg->buffer[0], 0x0000010000000000LL);
SET_U32_AT(mesg->buffer[8], 0);
#endif
MESSAGE_SET_ID(mesg->buffer, id);
ya_result qname_len = dnsname_len(qname);
u8 *tc = &mesg->buffer[DNS_HEADER_LENGTH];
memcpy(tc, qname, qname_len);
tc += qname_len;
SET_U16_AT(tc[0], qtype);
tc += 2;
SET_U16_AT(tc[0], qclass);
tc += 2;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->ar_start = tc;
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->send_length = tc - &mesg->buffer[0];
mesg->status = FP_MESG_OK;
mesg->rcode_ext = 0;
}
void
message_make_query_ex(message_data *mesg, u16 id, const u8 *qname, u16 qtype, u16 qclass, u16 flags)
{
#ifdef WORDS_BIGENDIAN
SET_U64_AT(mesg->buffer[0], 0x0000000000010000LL);
SET_U32_AT(mesg->buffer[8], 0);
#else
SET_U64_AT(mesg->buffer[0], 0x0000010000000000LL);
SET_U32_AT(mesg->buffer[8], 0);
#endif
MESSAGE_SET_ID(mesg->buffer, id);
ya_result qname_len = dnsname_len(qname);
u8 *tc = &mesg->buffer[DNS_HEADER_LENGTH];
memcpy(tc, qname, qname_len);
tc += qname_len;
SET_U16_AT(tc[0], qtype);
tc += 2;
SET_U16_AT(tc[0], qclass);
tc += 2;
mesg->ar_start = tc;
mesg->send_length = tc - &mesg->buffer[0];
mesg->rcode_ext = 0;
mesg->status = FP_MESG_OK;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
if(flags != 0)
{
SET_U16_AT(mesg->buffer[10], NETWORK_ONE_16);
mesg->rcode_ext |= MESSAGE_EDNS0_DNSSEC;
u8 *buffer = &mesg->buffer[mesg->send_length];
buffer[ 0] = 0;
buffer[ 1] = 0; // TYPE
buffer[ 2] = 0x29; //
buffer[ 3] = edns0_maxsize >> 8; // CLASS = SIZE
buffer[ 4] = edns0_maxsize; //
buffer[ 5] = (mesg->status >> 4); // extended RCODE & FLAGS
buffer[ 6] = mesg->rcode_ext >> 16;
buffer[ 7] = mesg->rcode_ext >> 8;
buffer[ 8] = mesg->rcode_ext;
buffer[ 9] = 0; // RDATA descriptor
buffer[10] = 0;
mesg->send_length += 11;
}
}
void message_make_message(message_data *mesg, u16 id, const u8 *qname, u16 qtype, u16 qclass, packet_writer *uninitialised_packet_writer)
{
assert(uninitialised_packet_writer != NULL);
#ifdef WORDS_BIGENDIAN
SET_U64_AT(mesg->buffer[0], 0x0000000000010000LL);
SET_U32_AT(mesg->buffer[8], 0);
#else
SET_U64_AT(mesg->buffer[0], 0x0000010000000000LL);
SET_U32_AT(mesg->buffer[8], 0);
#endif
MESSAGE_SET_ID(mesg->buffer, id);
packet_writer_create(uninitialised_packet_writer, mesg->buffer, DNSPACKET_MAX_LENGTH);
packet_writer_add_fqdn(uninitialised_packet_writer, qname);
packet_writer_add_u16(uninitialised_packet_writer, qtype);
packet_writer_add_u16(uninitialised_packet_writer, qclass);
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->send_length = packet_writer_get_offset(uninitialised_packet_writer);
mesg->ar_start = &mesg->buffer[mesg->send_length];
if(mesg->send_length < UDPPACKET_MAX_LENGTH)
{
mesg->size_limit = UDPPACKET_MAX_LENGTH;
}
else
{
mesg->size_limit = DNSPACKET_MAX_LENGTH;
}
mesg->status = FP_MESG_OK;
}
void
message_make_notify(message_data *mesg, u16 id, const u8 *qname, u16 qtype, u16 qclass)
{
#ifdef WORDS_BIGENDIAN
SET_U64_AT(mesg->buffer[0], 0x0000200000010000LL);
SET_U32_AT(mesg->buffer[8], 0);
#else
SET_U64_AT(mesg->buffer[0], 0x0000010000200000LL);
SET_U32_AT(mesg->buffer[8], 0);
#endif
MESSAGE_SET_ID(mesg->buffer, id);
ya_result qname_len = dnsname_len(qname);
u8 *tc = &mesg->buffer[DNS_HEADER_LENGTH];
memcpy(tc, qname, qname_len);
tc += qname_len;
SET_U16_AT(tc[0], qtype);
tc += 2;
SET_U16_AT(tc[0], qclass);
tc += 2;
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->ar_start = tc;
mesg->send_length = tc - &mesg->buffer[0];
mesg->status = FP_MESG_OK;
}
void
message_make_ixfr_query(message_data *mesg, u16 id, const u8 *qname, u32 soa_ttl, u16 soa_rdata_size, const u8 *soa_rdata)
{
packet_writer pw;
#ifdef WORDS_BIGENDIAN
SET_U64_AT(mesg->buffer[0], 0x0000000000010000LL);
SET_U32_AT(mesg->buffer[8], 0x00010000);
#else
SET_U64_AT(mesg->buffer[0], 0x0000010000000000LL);
SET_U32_AT(mesg->buffer[8], 0x00000100);
#endif
MESSAGE_SET_ID(mesg->buffer, id);
packet_writer_create(&pw, mesg->buffer, UDPPACKET_MAX_LENGTH);
packet_writer_add_fqdn(&pw, qname);
packet_writer_add_u16(&pw, TYPE_IXFR);
packet_writer_add_u16(&pw, CLASS_IN);
packet_writer_add_fqdn(&pw, qname);
packet_writer_add_u16(&pw, TYPE_SOA);
packet_writer_add_u16(&pw, CLASS_IN);
packet_writer_add_u32(&pw, htonl(soa_ttl));
packet_writer_add_rdata(&pw, TYPE_SOA, soa_rdata, soa_rdata_size);
#if DNSCORE_HAS_TSIG_SUPPORT
mesg->tsig.tsig = NULL;
#endif
mesg->ar_start = &mesg->buffer[packet_writer_get_offset(&pw)];
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->send_length = packet_writer_get_offset(&pw);
mesg->status = FP_MESG_OK;
}
#if DNSCORE_HAS_TSIG_SUPPORT
ya_result
message_sign_answer_by_name(message_data *mesg, const u8 *tsig_name)
{
const tsig_item *key = tsig_get(tsig_name);
return message_sign_answer(mesg, key);
}
ya_result
message_sign_query_by_name(message_data *mesg, const u8 *tsig_name)
{
const tsig_item *key = tsig_get(tsig_name);
return message_sign_query(mesg, key);
}
ya_result
message_sign_answer(message_data *mesg, const tsig_item *key)
{
if(key != NULL)
{
ZEROMEMORY(&mesg->tsig, sizeof(message_tsig));
mesg->tsig.tsig = key;
mesg->tsig.mac_size = mesg->tsig.tsig->mac_size;
u64 now = time(NULL);
mesg->tsig.timehi = htons((u16)(now >> 32));
mesg->tsig.timelo = htonl((u32)now);
mesg->tsig.fudge = htons(300); /* 5m */
mesg->tsig.mac_algorithm = key->mac_algorithm;
mesg->tsig.original_id = GET_U16_AT(mesg->buffer[0]);
return tsig_sign_answer(mesg);
}
return TSIG_BADKEY;
}
ya_result
message_sign_query(message_data *mesg, const tsig_item *key)
{
if(key != NULL)
{
ZEROMEMORY(&mesg->tsig, sizeof(message_tsig));
mesg->tsig.tsig = key;
mesg->tsig.mac_size = mesg->tsig.tsig->mac_size;
u64 now = time(NULL);
mesg->tsig.timehi = htons((u16)(now >> 32));
mesg->tsig.timelo = htonl((u32)now);
mesg->tsig.fudge = htons(300); /* 5m */
mesg->tsig.mac_algorithm = key->mac_algorithm;
mesg->tsig.original_id = GET_U16_AT(mesg->buffer[0]);
// mesg->tsig.error = 0; zeromem
// mesg->tsig.other_len = 0; zeromem
return tsig_sign_query(mesg);
}
return TSIG_BADKEY;
}
#endif
void
message_make_error(message_data *mesg, u16 error_code)
{
MESSAGE_FLAGS_OR(mesg->buffer, QR_BITS, error_code);
#ifdef WORDS_BIGENDIAN
SET_U32_AT(mesg->buffer[4], 0x00010000);
SET_U32_AT(mesg->buffer[8], 0x00000000);
#else
SET_U32_AT(mesg->buffer[4], 0x00000100);
SET_U32_AT(mesg->buffer[8], 0x00000000);
#endif
mesg->size_limit = UDPPACKET_MAX_LENGTH;
mesg->send_length = DNS_HEADER_LENGTH + 4;
mesg->send_length += dnsname_len(&mesg->buffer[DNS_HEADER_LENGTH]);
mesg->ar_start = &mesg->buffer[mesg->send_length];
mesg->status = (finger_print)error_code;
}
void
message_make_error_ext(message_data *mesg, u16 error_code)
{
yassert(FALSE); /// @todo 20140523 edf -- implement error code with OPT
}
ya_result
message_query_tcp(message_data *mesg, const host_address *server)
{
/* connect the server */
ya_result return_value;
if(ISOK(return_value = host_address2sockaddr(&mesg->other, server)))
{
int s;
if((s = socket(mesg->other.sa.sa_family, SOCK_STREAM, 0)) >=0)
{
socklen_t sa_len = return_value;
if(connect(s, (struct sockaddr*)&mesg->other, sa_len) == 0)
{
/// @todo 20140523 edf -- optionally sign the query
message_update_tcp_length(mesg);
ssize_t n;
#if DEBUG
log_debug("sending %d bytes to %{sockaddr} (tcp)", mesg->send_length, &mesg->other);
log_memdump_ex(g_system_logger, MSG_DEBUG5, mesg->buffer, mesg->send_length, 16, OSPRINT_DUMP_HEXTEXT);
#endif
if((n = writefully(s, &mesg->buffer_tcp_len[0], mesg->send_length + 2)) == mesg->send_length + 2)
{
u16 tcp_len;
if((n = readfully(s, &tcp_len, 2)) == 2)
{
tcp_len = ntohs(tcp_len);
if(readfully(s, mesg->buffer, tcp_len) == tcp_len)
{
/*
* test the answser
* test the TSIG if any
*/
mesg->received = tcp_len;
#if DEBUG
log_debug("received %d bytes from %{sockaddr} (tcp)", mesg->received, &mesg->other);
log_memdump_ex(g_system_logger, MSG_DEBUG5, mesg->buffer, mesg->received, 16, OSPRINT_DUMP_HEXTEXT);
#endif
return_value = message_process_lenient(mesg);
}
}
}
}
else
{
// Linux quirk ...
if(errno != EINPROGRESS)
{
return_value = ERRNO_ERROR;
}
else
{
return_value = MAKE_ERRNO_ERROR(ETIMEDOUT);
}
}
close_ex(s);
}
else
{
return_value = ERRNO_ERROR;
}
}
return return_value;
}
ya_result
message_query_tcp_ex(message_data *mesg, const host_address *server, message_data *answer)
{
/* connect the server */
ya_result return_value;
if(ISOK(return_value = host_address2sockaddr(&mesg->other, server)))
{
int s;
if((s = socket(mesg->other.sa.sa_family, SOCK_STREAM, 0)) >=0)
{
socklen_t sa_len = return_value;
if(connect(s, (struct sockaddr*)&mesg->other, sa_len) == 0)
{
/// @todo 20140523 edf -- optionally sign the query
message_update_tcp_length(mesg);
ssize_t n;
#if DEBUG
log_debug("sending %d bytes to %{sockaddr} (tcp)", mesg->send_length, &mesg->other);
log_memdump_ex(g_system_logger, MSG_DEBUG5, mesg->buffer, mesg->send_length, 16, OSPRINT_DUMP_HEXTEXT);
#endif
if((n = writefully(s, &mesg->buffer_tcp_len[0], mesg->send_length + 2)) == mesg->send_length + 2)
{
u16 tcp_len;
if((n = readfully(s, &tcp_len, 2)) == 2)
{
tcp_len = ntohs(tcp_len);
if(readfully(s, answer->buffer, tcp_len) == tcp_len)
{
/*
* test the answser
* test the TSIG if any
*/
answer->received = tcp_len;
#if DNSCORE_HAS_TSIG_SUPPORT
answer->tsig = mesg->tsig;
#endif
answer->other = mesg->other;
#if DEBUG
log_debug("received %d bytes from %{sockaddr} (tcp)", answer->received, &answer->other);
log_memdump_ex(g_system_logger, MSG_DEBUG5, answer->buffer, answer->received, 16, OSPRINT_DUMP_HEXTEXT);
#endif
return_value = message_process_lenient(answer);
}
}
}
}
else
{
// Linux quirk ...
if(errno != EINPROGRESS)
{
return_value = ERRNO_ERROR;
}
else
{
return_value = MAKE_ERRNO_ERROR(ETIMEDOUT);
}
}
close_ex(s);
}
else
{
return_value = ERRNO_ERROR;
}
}
return return_value;
}
ya_result
message_query_tcp_with_timeout(message_data *mesg, const host_address *address, u8 to_sec)
{
ya_result return_value;
/* ------------------------------------------------------------ */
mesg->buffer_tcp_len[0] = mesg->send_length >> 8;
mesg->buffer_tcp_len[1] = mesg->send_length;
input_stream is;
output_stream os;
if(ISOK(return_value = tcp_input_output_stream_connect_host_address(address, &is, &os, to_sec)))
{
if(ISOK(return_value = output_stream_write(&os, &mesg->buffer_tcp_len[0], mesg->send_length + 2)))
{
output_stream_flush(&os);
int fd = fd_input_stream_get_filedescriptor(&is);
tcp_set_sendtimeout(fd, 30, 0);
tcp_set_recvtimeout(fd, 30, 0);
#ifdef DEBUG
memset(mesg->buffer, 0xee, sizeof(mesg->buffer));
#endif
u16 len;
#ifdef DEBUG
len = ~0;
#endif
if(ISOK(return_value = input_stream_read_nu16(&is, &len)))
{
if (ISOK(return_value = input_stream_read_fully(&is, mesg->buffer, len)))
{
mesg->received = return_value;
}
}
}
output_stream_close(&os);
output_stream_close(&is);
}
return return_value;
}
ya_result
message_query_udp(message_data *mesg, const host_address *server)
{
ya_result return_code = SUCCESS;
int seconds = 0;
int useconds = 500000;
yassert(mesg != NULL);
yassert(server != NULL);
return_code = message_query_udp_with_time_out(mesg, server, seconds, useconds);
return return_code;
}
#define RESET_ID 1
#define CHANGE_NAME_SERVER 0
#if 1
ya_result
message_query_udp_with_time_out_and_retries(message_data *mesg, const host_address *server, int seconds, int useconds, u8 retries, u8 flags)
{
ya_result return_value = SUCCESS;
random_ctx rndctx = thread_pool_get_random_ctx();
u16 id;
for(u8 countdown = retries; countdown > 0; )
{
if (flags & RESET_ID)
{
id = (u16)random_next(rndctx);
MESSAGE_SET_ID(mesg->buffer, id);
}
else
{
id = MESSAGE_ID(mesg->buffer);
}
if(ISOK(return_value = message_query_udp_with_time_out(mesg, server, seconds, useconds)))
{
if(MESSAGE_ID(mesg->buffer) != id)
{
return_value = MESSAGE_HAS_WRONG_ID;
}
else if(!MESSAGE_QR(mesg->buffer))
{
return_value = MESSAGE_IS_NOT_AN_ANSWER;
}
else if(MESSAGE_RCODE(mesg->buffer) != RCODE_NOERROR)
{
return_value = MAKE_DNSMSG_ERROR(MESSAGE_RCODE(mesg->buffer));
}
else
{
return_value = INVALID_MESSAGE;
}
break;
}
if(return_value == MAKE_ERRNO_ERROR(EINTR))
{
continue;
}
if(return_value != MAKE_ERRNO_ERROR(EAGAIN) || countdown <= 0)
{
/*
* Do not retry for any other kind of error
*/
break;
}
countdown--;
usleep_ex(10000); /* 10 ms */
/*
if (flags & CHANGE_NAME_SERVER)
{
}
*/
}
return return_value;
}
#endif
ya_result
message_query_udp_with_time_out(message_data *mesg, const host_address *server, int seconds, int useconds)
{
yassert(mesg != NULL);
yassert(server != NULL);
/* connect the server */
ya_result return_value = SUCCESS;
socketaddress sa;
/* ------------------------------------------------------------ */
if(ISOK(return_value = host_address2sockaddr(&sa, server)))
{
int s;
if((s = socket(sa.sa.sa_family, SOCK_DGRAM, 0)) >=0)
{
socklen_t sa_len = return_value;
int n;
tcp_set_recvtimeout(s, seconds, useconds); /* half a second for UDP is a lot ... */
mesg->received = 0;
if((n = sendto(s, mesg->buffer, mesg->send_length, 0, &sa.sa, sa_len)) == mesg->send_length)
{
socketaddress ans_sa;
socklen_t ans_sa_len = sizeof(ans_sa);
while((n = recvfrom(s, mesg->buffer, sizeof(mesg->buffer), 0, &ans_sa.sa, &ans_sa_len)) >= 0)
{
/* check that the sender is the one we spoke to */
#ifdef DEBUG
log_memdump_ex(g_system_logger, MSG_DEBUG5, mesg->buffer, n, 16, OSPRINT_DUMP_HEXTEXT);
#endif
if(sockaddr_equals(&sa.sa, &ans_sa.sa))
{
mesg->received = n;
return_value = message_process_lenient(mesg);
break;
}
}
if(n < 0)
{
return_value = ERRNO_ERROR;
}
/* timeout */
}
else
{
return_value = (n < 0)?ERRNO_ERROR:ERROR;
}
close_ex(s);
}
else
{
return_value = ERRNO_ERROR;
}
}
return return_value;
}
ya_result
message_ixfr_query_get_serial(const message_data *mesg, u32 *serial)
{
packet_unpack_reader_data purd;
ya_result return_value;
u8 domain_fqdn[MAX_DOMAIN_LENGTH];
u8 soa_fqdn[MAX_DOMAIN_LENGTH];
purd.packet = (u8*)mesg->buffer;
purd.packet_size = mesg->received;
purd.offset = DNS_HEADER_LENGTH;
/* Keep only the query */
if(ISOK(return_value = packet_reader_read_fqdn(&purd, domain_fqdn, sizeof(domain_fqdn))))
{
purd.offset += 4;
/* Get the queried serial */
if(ISOK(return_value = packet_reader_read_fqdn(&purd, soa_fqdn, sizeof(soa_fqdn))))
{
if(dnsname_equals(domain_fqdn, soa_fqdn))
{
u16 soa_type;
u16 soa_class;
u32 soa_ttl;
u16 soa_rdata_size;
u32 soa_serial;
if(ISOK(return_value = packet_reader_read_u16(&purd, &soa_type)))
{
if(soa_type == TYPE_SOA)
{
packet_reader_read_u16(&purd, &soa_class);
packet_reader_read_u32(&purd, &soa_ttl);
packet_reader_read_u16(&purd, &soa_rdata_size);
packet_reader_skip_fqdn(&purd);
packet_reader_skip_fqdn(&purd);
packet_reader_read_u32(&purd, &soa_serial);
*serial=ntohl(soa_serial);
}
}
}
}
}
return return_value;
}
ya_result
message_query_serial(const u8 *origin, const host_address *server, u32 *serial_out)
{
yassert(origin != NULL);
yassert(server != NULL);
yassert(serial_out != NULL);
/* do an SOA query */
ya_result return_value;
random_ctx rndctx = thread_pool_get_random_ctx();
message_data soa_query_mesg;
for(u16 countdown = 5; countdown > 0; )
{
u16 id = (u16)random_next(rndctx);
message_make_query(&soa_query_mesg, id, origin, TYPE_SOA, CLASS_IN);
if(ISOK(return_value = message_query_udp(&soa_query_mesg, server)))
{
u8 *buffer = soa_query_mesg.buffer;
if((MESSAGE_ID(buffer) == id) && MESSAGE_QR(buffer) &&(MESSAGE_RCODE(buffer) == RCODE_NOERROR) && (MESSAGE_QD(buffer) == NETWORK_ONE_16)&& ((MESSAGE_AN(buffer) == NETWORK_ONE_16) || (MESSAGE_NS(buffer) == NETWORK_ONE_16)))
{
packet_unpack_reader_data reader;
packet_reader_init(&reader, buffer, soa_query_mesg.received); // scan-build false positive: if message_query_udp returns no-error, then soa_query_mesg.received is set
reader.offset = DNS_HEADER_LENGTH;
packet_reader_skip_fqdn(&reader);
packet_reader_skip(&reader, 4);
u8 tmp[MAX_DOMAIN_LENGTH];
/* read and expect an SOA */
packet_reader_read_fqdn(&reader, tmp, sizeof(tmp));
if(dnsname_equals(tmp, origin))
{
struct type_class_ttl_rdlen tctr;
if(packet_reader_read(&reader, &tctr, 10) == 10)
{
if((tctr.qtype == TYPE_SOA) && (tctr.qclass == CLASS_IN))
{
if(ISOK(return_value = packet_reader_skip_fqdn(&reader)))
{
if(ISOK(return_value = packet_reader_skip_fqdn(&reader)))
{
if(packet_reader_read(&reader, tmp, 4) == 4)
{
*serial_out = ntohl(GET_U32_AT(tmp[0]));
return SUCCESS;
}
}
}
}
else
{
return MESSAGE_UNEXPECTED_ANSWER_TYPE_CLASS;
}
}
}
return_value = MESSAGE_UNEXPECTED_ANSWER_DOMAIN;
}
else if(MESSAGE_ID(buffer) != id)
{
return_value = MESSAGE_HAS_WRONG_ID;
}
else if(!MESSAGE_QR(buffer))
{
return_value = MESSAGE_IS_NOT_AN_ANSWER;
}
else if(MESSAGE_RCODE(buffer) != RCODE_NOERROR)
{
return_value = MAKE_DNSMSG_ERROR(MESSAGE_RCODE(buffer));
}
else
{
return_value = INVALID_MESSAGE;
}
break;
}
if(return_value == MAKE_ERRNO_ERROR(EINTR))
{
continue;
}
if(return_value != MAKE_ERRNO_ERROR(EAGAIN) || countdown <= 0)
{
/*
* Do not retry for any other kind of error
*/
break;
}
countdown--;
usleep_ex(10000); /* 10 ms */
}
return return_value;
}
/*
* Does not clone the pool.
*/
message_data*
message_dup(message_data *mesg)
{
message_data *clone = mesg;
MALLOC_OR_DIE(message_data*, clone, sizeof(message_data), MESGDATA_TAG);
memcpy(clone, mesg, offsetof(message_data,qname));
dnsname_copy(clone->qname, mesg->qname);
memcpy(clone->buffer_tcp_len, mesg->buffer_tcp_len, 2 + MAX(mesg->received, mesg->send_length));
return clone;
}
/* ------------------------------------------------------------ */
/** @} */
/*----------------------------------------------------------------------------*/
You can’t perform that action at this time.