Andrew Boyson / net

Dependents:   oldheating gps motorhome heating

udp/dns/dnsserver.cpp

Committer:
andrewboyson
Date:
2017-09-25
Revision:
36:900e24b27bfb
Parent:
33:714a0345e59b
Child:
37:793b39683406

File content as of revision 36:900e24b27bfb:

#include    "mbed.h"
#include  "dnshdr.h"
#include "dnsname.h"
#include     "net.h"
#include     "dns.h"
#include     "log.h"
#include    "dhcp.h"
#include   "slaac.h"
#include      "io.h"

#define DEBUG true

//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   questionTypes[4];
static int   nodeNameTypes[4];
static bool  mdnsUnicastReply;

static int initialise(int dnsProtocol, int size)
{    
    if (         size > 512) { if (DEBUG) LogTimeF("DnsServer-initialise length %d too long\r\n",  size         ); return -1; }
    if (DnsHdrQdcount >   4) { if (DEBUG) LogTimeF("DnsServer-initialise too many queries %d\r\n", DnsHdrQdcount); return -1; }
    
    p = DnsHdrData;
    
    myFullNameLength = DnsMakeFullNameFromName(dnsProtocol, NetName, sizeof(myFullName), myFullName);
    
    return 0;
}
static int readQuestions()
{
    char iEncodedName;
    mdnsUnicastReply = false;
    
    //Get the questions
    for (int q = 0; q < DnsHdrQdcount; q++)
    {
        iEncodedName = DnsNameIndexFromPointer(p);
        int nameLength = DnsNameLength(p);
        if (!nameLength) { if (DEBUG) LogTimeF("DnsServer-readQuestions namelength is zero\r\n"); return -1; }
        nodeNameTypes[q] = DNS_RECORD_NONE;
        if (!nodeNameTypes[q] && DnsNameCompare   (p, myFullName)      ) nodeNameTypes[q] = DNS_RECORD_PTR;  //rtc.local
        if (!nodeNameTypes[q] && DnsNameCompareIp4(p, DhcpLocalIp)     ) nodeNameTypes[q] = DNS_RECORD_A;    //3.1.168.192.inaddr.arpa
        if (!nodeNameTypes[q] && DnsNameCompareIp6(p, SlaacLinkLocalIp)) nodeNameTypes[q] = DNS_RECORD_AAAA; //5.8.c.c.0.1.e.f.f.f.2.3.1.1.2.0.8.7.d.0.9.0.f.1.0.7.4.0.1.0.0.2.ip6.arpa
        p += nameLength;                            //Skip past the name
        p++ ;                                       //skip the first byte of the type
        char recordType = *p++;                     //read the record type
        if (*p++ & 0x80) mdnsUnicastReply = true;   //Check the 15th bit (UNICAST-RESPONSE)
        p += 1;                                     //skip the class
        
        questionTypes[q] = recordType;
        
        if (DEBUG)
        {
            switch (recordType)
            {
                case DNS_RECORD_A:    LogF("  for IP4 address of "); break;
                case DNS_RECORD_PTR:  LogF("  for name of ");        break;
                case DNS_RECORD_AAAA: LogF("  for IP6 address of "); break;
                default:              LogF("  for unrecognised record type %d of ", recordType); break;
            }
            char text[256];
            DnsNameDecode(iEncodedName, sizeof(text), text);
            LogF("%s\r\n", text);
        }    
    }
    return 0;
}
static int addAnswers(int dnsProtocol)
{
    DnsHdrAncount = 0;
    for (int q = 0; q < DnsHdrQdcount; q++)
    {
        if (DEBUG) LogF("  deal with question %d, answer %d, question %d, node %d\r\n", q, DnsHdrAncount, questionTypes[q], nodeNameTypes[q]);
        
        //Skip unwanted record types
        switch (questionTypes[q])
        {
            case DNS_RECORD_A:
            case DNS_RECORD_AAAA:
            case DNS_RECORD_PTR: break;
            default: continue;
        }
        if (!nodeNameTypes[q]) continue; //Skip queries which are not addressed to me
        if (p - DnsHdrPacket > 500)
        {
            LogTimeF("DnsServer-addAnswers Ip4 query reply is getting too big\r\n");
            return -1;
        }
        DnsHdrAncount++;
        int lenPayload = 0;
        char* pPayload = 0;
        switch (questionTypes[q])
        {
            case DNS_RECORD_A:
                if (DEBUG) Log("  replied with my IPv4 address\r\n");
                DnsNameEncode(myFullName, &p);
                  pPayload = (char*)&DhcpLocalIp;
                lenPayload = 4;
                break;
                
            case DNS_RECORD_AAAA:
                if (DEBUG) Log("  replied with my IPv6 address\r\n");
                DnsNameEncode(myFullName, &p);
                  pPayload = SlaacLinkLocalIp;
                lenPayload = 16;
                break;
                
            case DNS_RECORD_PTR:
                if (DEBUG) Log("  replied with my name\r\n");
                if (nodeNameTypes[q] == DNS_RECORD_A   ) DnsNameEncodeIp4(DhcpLocalIp,      &p);
                if (nodeNameTypes[q] == DNS_RECORD_AAAA) DnsNameEncodeIp6(SlaacLinkLocalIp, &p);
                  pPayload = myFullName;
                lenPayload = myFullNameLength;
                break;
        }
        char mdns = dnsProtocol == DNS_PROTOCOL_MDNS ? 0x80 : 0; //Set the 15th bit (CACHE_FLUSH) of the class to 1 if MDNS
        *p++ =    0; *p++ = questionTypes[q];                    //16 bit Type
        *p++ = mdns; *p++ = 1;                                   //16 bit Class LSB QCLASS_IN = 1 - internet
        *p++ =    0; *p++ = 0; *p++ = 4; *p++ = 0;               //32 bit TTL seconds - 1024
        *p++ =    0; *p++ = lenPayload;                          //16 bit length in bytes
        memcpy(p, pPayload, lenPayload);                         //Copy the payload
        p += lenPayload;                                         //Adjust the pointer to the next character after the payload

    }
    return 0;
}

int DnsServerHandleQuery(int dnsProtocol, int *pSize) //Received an mdns or llmnr query on port 5353 or 5355
{
    if (DEBUG) DnsHdrLog("DnsServer received query", dnsProtocol);
    
    if (initialise(dnsProtocol, *pSize)) return DO_NOTHING;
    if (readQuestions()                ) return DO_NOTHING;
    if (addAnswers(dnsProtocol)        ) return DO_NOTHING;
    if (!DnsHdrAncount)                  return DO_NOTHING;
    
    DnsHdrIsReply         = true;
    DnsHdrIsAuthoritative = true;
    DnsHdrWrite();
    
    *pSize = p - DnsHdrPacket;
    
    if (DEBUG) DnsHdrLog("DnsServer sending reply", dnsProtocol);

    if (dnsProtocol == DNS_PROTOCOL_MDNS && !mdnsUnicastReply) return MULTICAST_MDNS;
    return UNICAST;
}