Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
yadifa/lib/dnscore/src/message.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
2301 lines (1833 sloc)
66.1 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*------------------------------------------------------------------------------ | |
* | |
* 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; | |
} | |
/* ------------------------------------------------------------ */ | |
/** @} */ | |
/*----------------------------------------------------------------------------*/ |