A stack which works with or without an Mbed os library. Provides IPv4 or IPv6 with a full 1500 byte buffer.

Dependents:   oldheating gps motorhome heating

udp/dns/dnsserver.c

Committer:
andrewboyson
Date:
2021-05-20
Revision:
200:5acbc41bf469
Parent:
190:c4415a7253f5

File content as of revision 200:5acbc41bf469:

#include <stdbool.h>
#include <string.h>

#include      "dns.h"
#include   "dnshdr.h"
#include  "dnsname.h"
#include "dnslabel.h"
#include      "net.h"
#include "net-this.h"
#include   "action.h"
#include      "log.h"
#include     "dhcp.h"
#include    "slaac.h"
#include      "ip4.h"
#include      "ip6.h"

bool DnsServerTrace = false;

#define RECORD_NONE               0
#define RECORD_PTR4               1
#define RECORD_PTR6_LINK_LOCAL    2
#define RECORD_PTR6_UNIQUE_LOCAL  3
#define RECORD_PTR6_GLOBAL        4
#define RECORD_A                  5
#define RECORD_AAAA_LINK_LOCAL    6
#define RECORD_AAAA_UNIQUE_LOCAL  7
#define RECORD_AAAA_GLOBAL        8

#define MAX_ANSWERS 4

//Set by 'initialise'
static char* p;               //Position relative to DnsHdrData and is updated while both reading questions and writing answers
static char  myFullName[100]; //The name, adjusted to include the domain if needed by the protocol, used when reading and when writing
static int   myFullNameLength;

//Set by readQuestions and used by answerQuestions
static int   answers[MAX_ANSWERS];
static int   answerCount = 0;
static bool  mdnsUnicastReply;

static int readQuestions()
{
    p = DnsHdrData;
    mdnsUnicastReply = false;
    
    //Get the questions
    answerCount = 0;
    for (int i = 0; i < DnsHdrQdcount; i++)
    {
        //Bomb out if there are too many answers
        if (answerCount >= MAX_ANSWERS)
        {
            if (DnsServerTrace) LogTimeF("DnsServer-readQuestions - exceeded %d answers\r\n", MAX_ANSWERS);
            break;
        }
        
        //Bomb out if we are tending to overrun the buffer
        if (p - DnsHdrData > DnsHdrDataLength)
        {
            if (DnsServerTrace) LogTimeF("DnsServer-readQuestions - overrunning the buffer of %d bytes\r\n", DnsHdrDataLength);
            return -1;
        }
        
        //Node name
        int nameLength = DnsNameLength(p);
        if (!nameLength)
        {
            if (DnsServerTrace) LogTimeF("DnsServer-readQuestions namelength is zero\r\n");
            return -1;
        }
        bool nodeIsName  = DnsNameComparePtr(p, myFullName);
        bool nodeIsAddr4 = DnsNameCompareIp4(p, DhcpLocalIp);
        bool nodeIsLocl6 = DnsNameCompareIp6(p, SlaacLinkLocalIp);
        bool nodeIsUniq6 = DnsNameCompareIp6(p, SlaacUniqueLocalIp);
        bool nodeIsGlob6 = DnsNameCompareIp6(p, SlaacGlobalIp);
        p += nameLength;                          //Skip past the name
                
        //Type
        p++ ;                                     //skip the first byte of the type
        char recordType = *p++;                   //read the record type
        
        //Class
        if (*p++ & 0x80) mdnsUnicastReply = true; //check the class 15th bit (UNICAST-RESPONSE)
        p++;                                      //skip the class
        
        //Handle the questions
        if (nodeIsName && recordType == DNS_RECORD_A   )   answers[answerCount++] = RECORD_A;
        if (nodeIsName && recordType == DNS_RECORD_AAAA)
        {
                                                           answers[answerCount++] = RECORD_AAAA_LINK_LOCAL;
                                if (SlaacUniqueLocalIp[0]) answers[answerCount++] = RECORD_AAAA_UNIQUE_LOCAL;
                                if (SlaacGlobalIp     [0]) answers[answerCount++] = RECORD_AAAA_GLOBAL;
        }
        if (nodeIsAddr4 && recordType == DNS_RECORD_PTR )  answers[answerCount++] = RECORD_PTR4;
        if (nodeIsLocl6 && recordType == DNS_RECORD_PTR )  answers[answerCount++] = RECORD_PTR6_LINK_LOCAL;
        if (nodeIsUniq6 && recordType == DNS_RECORD_PTR )  answers[answerCount++] = RECORD_PTR6_UNIQUE_LOCAL;
        if (nodeIsGlob6 && recordType == DNS_RECORD_PTR )  answers[answerCount++] = RECORD_PTR6_GLOBAL;
    }
    return 0;
}
static int addAnswers(int dnsProtocol)
{    
    //Go through each answer
    DnsHdrAncount = 0;
    for (int i = 0; i < answerCount; i++)
    {                
        //Bomb out if we are tending to overrun the buffer
        if (p - DnsHdrData > DnsHdrDataLength)
        {
            if (DnsServerTrace) LogTimeF("DnsServer-addAnswers - reply is getting too big\r\n");
            return -1;
        }
                
        //Encode the node name
        switch (answers[i])
        {
            case RECORD_A:                 DnsNameEncodePtr(myFullName,         &p); break;
            case RECORD_AAAA_LINK_LOCAL:   DnsNameEncodePtr(myFullName,         &p); break;
            case RECORD_AAAA_UNIQUE_LOCAL: DnsNameEncodePtr(myFullName,         &p); break;
            case RECORD_AAAA_GLOBAL:       DnsNameEncodePtr(myFullName,         &p); break;
            case RECORD_PTR4:              DnsNameEncodeIp4(DhcpLocalIp,        &p); break;
            case RECORD_PTR6_LINK_LOCAL:   DnsNameEncodeIp6(SlaacLinkLocalIp,   &p); break;
            case RECORD_PTR6_UNIQUE_LOCAL: DnsNameEncodeIp6(SlaacUniqueLocalIp, &p); break;
            case RECORD_PTR6_GLOBAL:       DnsNameEncodeIp6(SlaacGlobalIp,      &p); break;
        }
        
        //Add the 16 bit type
        *p++ = 0;
        switch (answers[i])
        {
            case RECORD_A:                 *p++ = DNS_RECORD_A;    break;
            case RECORD_AAAA_LINK_LOCAL:   *p++ = DNS_RECORD_AAAA; break;
            case RECORD_AAAA_UNIQUE_LOCAL: *p++ = DNS_RECORD_AAAA; break;
            case RECORD_AAAA_GLOBAL:       *p++ = DNS_RECORD_AAAA; break;
            case RECORD_PTR4:              *p++ = DNS_RECORD_PTR;  break;
            case RECORD_PTR6_LINK_LOCAL:   *p++ = DNS_RECORD_PTR;  break;
            case RECORD_PTR6_UNIQUE_LOCAL: *p++ = DNS_RECORD_PTR;  break;
            case RECORD_PTR6_GLOBAL:       *p++ = DNS_RECORD_PTR;  break;
        }
        
        //Add the class
        char mdns = dnsProtocol == DNS_PROTOCOL_MDNS ? 0x80 : 0; //Set the 15th bit (CACHE_FLUSH) of the class to 1 if MDNS
        *p++ = mdns; *p++ = 1;                                   //16 bit Class LSB QCLASS_IN = 1 - internet
        
        //Add the TTL
        *p++ =    0; *p++ = 0; *p++ = 4; *p++ = 0;               //32 bit TTL seconds - 1024
        
        
        //Add the 16 bit payload length
        *p++ = 0;
        switch (answers[i])
        {
            case RECORD_A:                 *p++ =  4;                   break;
            case RECORD_AAAA_LINK_LOCAL:   *p++ = 16;                   break;
            case RECORD_AAAA_UNIQUE_LOCAL: *p++ = 16;                   break;
            case RECORD_AAAA_GLOBAL:       *p++ = 16;                   break;
            case RECORD_PTR4:              *p++ = myFullNameLength + 2; break; //add a byte for the initial length and another for the terminating zero length
            case RECORD_PTR6_LINK_LOCAL:   *p++ = myFullNameLength + 2; break;
            case RECORD_PTR6_UNIQUE_LOCAL: *p++ = myFullNameLength + 2; break;
            case RECORD_PTR6_GLOBAL:       *p++ = myFullNameLength + 2; break;
        }
        
        //Add the payload
        switch (answers[i])
        {
            case RECORD_A:                 memcpy(p, &DhcpLocalIp,        4); p +=  4; break;
            case RECORD_AAAA_LINK_LOCAL:   memcpy(p, SlaacLinkLocalIp,   16); p += 16; break;
            case RECORD_AAAA_UNIQUE_LOCAL: memcpy(p, SlaacUniqueLocalIp, 16); p += 16; break;
            case RECORD_AAAA_GLOBAL:       memcpy(p, SlaacGlobalIp,      16); p += 16; break;
            case RECORD_PTR4:              DnsNameEncodePtr(myFullName, &p);           break;
            case RECORD_PTR6_LINK_LOCAL:   DnsNameEncodePtr(myFullName, &p);           break;
            case RECORD_PTR6_UNIQUE_LOCAL: DnsNameEncodePtr(myFullName, &p);           break;
            case RECORD_PTR6_GLOBAL:       DnsNameEncodePtr(myFullName, &p);           break;
        }
        //Increment the number of good answers to send
        DnsHdrAncount++;
    }
    return 0;
}

int DnsServerHandleQuery(void (*traceback)(void), int dnsProtocol, void* pPacketTx, int *pSizeTx) //Received an mdns or llmnr query on port 5353 or 5355
{            
    myFullNameLength = DnsLabelMakeFullNameFromName(dnsProtocol, NET_NAME, sizeof(myFullName), myFullName);
    
    if (readQuestions()) return DO_NOTHING;
    if (!answerCount) return DO_NOTHING;
    
    if (DnsServerTrace || NetTraceHostGetMatched())
    {
        if (NetTraceNewLine) Log("\r\n");
        LogTimeF("DnsServer received query\r\n");
        if (NetTraceStack) traceback();
        DnsHdrLog(dnsProtocol);
    }
    
    char* pRx = DnsHdrData;
    char* pEndRx = p;
    int qdcount = DnsHdrQdcount;
    int nscount = DnsHdrNscount;
    int arcount = DnsHdrArcount;
    
    DnsHdrSetup(pPacketTx, *pSizeTx);
    p = DnsHdrData; 
    
    //Add the questions if this is not MDNS
    if (dnsProtocol == DNS_PROTOCOL_MDNS)
    {
        DnsHdrQdcount = 0;
        DnsHdrNscount = 0;
        DnsHdrArcount = 0;
    }
    else
    {
        DnsHdrQdcount = qdcount;
        DnsHdrNscount = nscount;
        DnsHdrArcount = arcount;
        while (pRx < pEndRx) *p++ = *pRx++;
    }
    

    if (addAnswers(dnsProtocol)) return DO_NOTHING;
    
    DnsHdrIsReply          = true;
    DnsHdrIsAuthoritative  = false;
    if (dnsProtocol == DNS_PROTOCOL_MDNS) DnsHdrIsAuthoritative  = true; //See rfc6762 18.4
    DnsHdrIsRecursiveQuery = false;
    
    DnsHdrWrite();
    
    *pSizeTx = p - DnsHdrPacket;
    
    if (DnsServerTrace || NetTraceHostGetMatched()) DnsHdrLog(dnsProtocol);

    int dest;
    if (dnsProtocol == DNS_PROTOCOL_MDNS && !mdnsUnicastReply) dest = MULTICAST_MDNS;
    else                                                       dest =   UNICAST;
    
    return ActionMakeFromDestAndTrace(dest, (NetTraceStack && DnsServerTrace) || NetTraceHostGetMatched());
}