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

resolve/nr4.c

Committer:
andrewboyson
Date:
2020-04-02
Revision:
167:3ba4e3c49631
Parent:
159:3ebef2d02f7f
Child:
170:96c637dc3f52

File content as of revision 167:3ba4e3c49631:

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

#include      "log.h"
#include  "mstimer.h"
#include      "net.h"
#include      "mac.h"
#include  "ip4addr.h"
#include     "dhcp.h"
#include      "dns.h"
#include "dnsquery.h"
#include "dnslabel.h"
#include     "http.h"

bool Nr4Trace = false;

#define NAME_MAX_LENGTH      20
#define  CACHE_TIMEOUT_MS  3600 * 1000
#define FREEZE_TIMEOUT_MS  1800 * 1000
#define  REPLY_TIMEOUT_MS     1 * 1000

#define RECORDS_COUNT 20

#define STATE_EMPTY 0
#define STATE_WANT  1
#define STATE_SENT  2
#define STATE_VALID 3

#define TODO_NONE         0
#define TODO_NAME_FROM_IP 1
#define TODO_IP_FROM_NAME 2

struct record
{
    uint32_t elapsed;
    uint32_t ip;
    uint8_t  todo;
    uint8_t  state;
    uint8_t  protocol;
    char     name[NAME_MAX_LENGTH];
};
static struct record records[RECORDS_COUNT];

static int getExistingIp(uint32_t ip)
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (records[i].ip == ip) return i;
    }
    return -1;
}
static int getExistingName(char* name)
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (DnsLabelIsSame(records[i].name, name)) return i;
    }
    return -1;
}
static int getNameOnly(char* name)
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (records[i].ip) continue;
        if (DnsLabelIsSame(records[i].name, name)) return i;
    }
    return -1;
}
static int getOldest()
{
    int        iOldest = 0;
    uint32_t ageOldest = 0;
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) return i; //Found an empty slot so just return it
        uint32_t age = MsTimerCount - records[i].elapsed;
        if (age >= ageOldest)
        {
            ageOldest = age;
              iOldest = i;
        }
    }  
    return iOldest;                            //Otherwise return the oldest
}
void Nr4MakeRequestForNameFromIp(uint32_t ip)
{
    //Don't treat non ips
    if (!ip) return;
    int i;
    
    //If a record already exists then request an update
    i = getExistingIp(ip);
    if (i > -1)
    {
        if (!MsTimerRelative(records[i].elapsed, FREEZE_TIMEOUT_MS)) return;
        if (Nr4Trace)
        {
            LogTimeF("NR - renew name of ");
            Ip4AddressLog(ip);
            Log("\r\n");
        }
        records[i].todo     = TODO_NAME_FROM_IP;
        records[i].state    = STATE_WANT;
        records[i].protocol = DnsGetNextProtocol4(DNS_PROTOCOL_NONE);
        records[i].elapsed  = MsTimerCount;
        return;
    }
    
    //If a record does not exist then find the first empty slot and add the IP and date
    if (Nr4Trace)
    {
        LogTimeF("NR - request name of ");
        Ip4AddressLog(ip);
        Log("\r\n");
    }
    i = getOldest();
    records[i].ip       = ip;
    records[i].todo     = TODO_NAME_FROM_IP;
    records[i].state    = STATE_WANT;
    records[i].protocol = DnsGetNextProtocol4(DNS_PROTOCOL_NONE);
    records[i].elapsed  = MsTimerCount;
    records[i].name[0]  = 0;
}
void Nr4MakeRequestForIpFromName(char* name)
{
    //Don't treat non names
    if (!name[0]) return;
    int i;
        
    //If a record already exists then request an update
    i = getExistingName(name);
    if (i > -1)
    {
        if (!MsTimerRelative(records[i].elapsed, FREEZE_TIMEOUT_MS)) return;
        if (Nr4Trace)
        {
            LogTimeF("NR - renew IPv4 of %s\r\n", name);
        }
        records[i].todo     = TODO_IP_FROM_NAME;
        records[i].state    = STATE_WANT;
        records[i].protocol = DnsGetNextProtocol4(DNS_PROTOCOL_NONE);
        records[i].elapsed  = MsTimerCount;
        return;
    }
    
    //If a record does not exist then find the first empty slot and add the name and date
    if (Nr4Trace)
    {
        LogTimeF("NR - request IPv4 of %s\r\n", name);
    }
    i = getOldest();
    records[i].ip       = 0;
    records[i].todo     = TODO_IP_FROM_NAME;
    records[i].state    = STATE_WANT;
    records[i].protocol = DnsGetNextProtocol4(DNS_PROTOCOL_NONE);
    records[i].elapsed  = MsTimerCount;
    strncpy(records[i].name, name, NAME_MAX_LENGTH);
    records[i].name[NAME_MAX_LENGTH - 1] = 0;
}
static void addIpRecord(int i, uint32_t ip, char* name, int protocol)
{
    records[i].todo     = TODO_NONE;
    records[i].elapsed  = MsTimerCount;
    records[i].ip       = ip;
    records[i].protocol = protocol;
    records[i].state    = STATE_VALID;
    strncpy(records[i].name, name, NAME_MAX_LENGTH);
    records[i].name[NAME_MAX_LENGTH - 1] = 0;
}
void Nr4AddIpRecord(uint32_t ip, char* name, int protocol)
{    
    int i;
    
    //Get existing ip and, if found, add it then clear any name only entries
    i = getExistingIp(ip);
    if (i >= 0)
    {
        if (Nr4Trace)
        {
            if (DnsLabelIsSame(name, records[i].name)) LogTimeF("NR - confirm existing ");
            else                                       LogTimeF("NR - replace name for existing ip ");
            Ip4AddressLog(ip);
            Log(" == '");
            Log(name);
            Log("'\r\n");
        }
        addIpRecord(i, ip, name, protocol);
        
        i = getNameOnly(name);
        if (i >= 0)
        {
            if (Nr4Trace) LogTimeF("NR - clear name '%s' with no ip\r\n", name);
            records[i].state = STATE_EMPTY;
        }
        return;
    }
    
    //Get name only entry and, if found, add it
    i = getNameOnly(name);
    if (i >= 0)
    {
        if (Nr4Trace)
        {
            LogTimeF("NR - add ip for name ");
            Ip4AddressLog(ip);
            Log(" == '");
            Log(name);
            Log("'\r\n");
        }
        addIpRecord(i, ip, name, protocol);
        return;
    }
    
    //No other entry exists so just add it to the next available space
    i = getOldest();
    if (Nr4Trace)
    {
        LogTimeF("NR - add ip for name ");
        Ip4AddressLog(ip);
        Log(" == '");
        Log(name);
        Log("'\r\n");
    }
    addIpRecord(i, ip, name, protocol);    
}
void Nr4IpToName(uint32_t ip, char* name)
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (records[i].ip == ip)
        {
            strcpy(name, records[i].name);
            return;
        }
    }
    name[0] = 0;
}
void Nr4NameToIp(char* name, uint32_t* pIp)
{
    uint32_t newest = 0xFFFFFFFF;
    *pIp = 0;
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (!records[i].ip) continue;
        if (!DnsLabelIsSame(records[i].name, name)) continue;
        uint32_t age = MsTimerCount - records[i].elapsed;
        if (age <= newest)
        {
            newest = age;
            *pIp = records[i].ip;
        }
    }  
}
bool Nr4HaveIpForName(char* name)
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (!records[i].ip) continue;
        if (!DnsLabelIsSame(records[i].name, name)) return true;
    }
    return false;
}
static char letterFromStateAndProtocol(uint8_t dnsState, uint8_t protocol)
{
    switch (dnsState)
    {
        case STATE_SENT:
        case STATE_WANT:                 return '>';
        
        case STATE_VALID:
            switch (protocol)
            {
                case DNS_PROTOCOL_UDNS:  return 'd';
                case DNS_PROTOCOL_MDNS:  return 'm';
                case DNS_PROTOCOL_LLMNR: return 'l';
                case DNS_PROTOCOL_NONE:  return '-';
                default:                 return '?';
            }
        default:                         return '~';
    }
}
void Nr4SendHttp()
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (records[i].ip || records[i].name[0])
        {
            HttpAddF("%4u ", (MsTimerCount - records[i].elapsed) / 1000 / 60);
            
            int ipLen = Ip4AddressHttp(records[i].ip);
            HttpAddFillChar(' ', 40 - ipLen);
                        
            HttpAddChar(letterFromStateAndProtocol(records[i].state, records[i].protocol));
            
            HttpAddChar(' ');
            
            HttpAddText(records[i].name);
            
            HttpAddChar('\r');
            HttpAddChar('\n');
        }
    }
}
void Nr4SendAjax()
{
    for (int i = 0; i < RECORDS_COUNT; i++)
    {
        if (records[i].state == STATE_EMPTY) continue;
        if (records[i].ip || records[i].name[0])
        {
            HttpAddByteAsHex(i);
            HttpAddChar('\t');
            HttpAddInt32AsHex(MsTimerCount - records[i].elapsed);
            HttpAddChar('\t');
            HttpAddInt32AsHex(records[i].ip);
            HttpAddChar('\t');
            HttpAddChar(letterFromStateAndProtocol(records[i].state, records[i].protocol));
            HttpAddChar('\t');
            HttpAddText(records[i].name);
            HttpAddChar('\n');
        }
    }
}
static void clearCache(struct record* pr)
{
    if (MsTimerRelative(pr->elapsed, CACHE_TIMEOUT_MS)) pr->state = STATE_EMPTY;
}
static void nextProtocol(struct record* pr)
{
    if (pr->state == STATE_SENT && MsTimerRelative(pr->elapsed, REPLY_TIMEOUT_MS) && pr->protocol)
    {
        pr->protocol = DnsGetNextProtocol4(pr->protocol);
        if (pr->protocol)
        {
            pr->state = STATE_WANT;
        }
        else
        {
            if (pr->todo == TODO_NAME_FROM_IP) pr->name[0] = 0;
            if (pr->todo == TODO_IP_FROM_NAME) pr->ip = 0;
            pr->state  = STATE_VALID;
        }
        pr->elapsed = MsTimerCount;
    }
}
static void queryNameFromIp(struct record* pr)
{
    if (Nr4Trace)
    {
        LogTime("NR - send ");
        DnsProtocolLog(pr->protocol);
        Log(" request for name from IP4 ");
        Ip4AddressLog(pr->ip);
        Log("\r\n");
    }
    DnsQueryNameFromIp4(pr->ip, pr->protocol);
}
static void queryIpFromName(struct record* pr)
{
    if (Nr4Trace)
    {
        LogTime("NR - send ");
        DnsProtocolLog(pr->protocol);
        Log(" request for IP4 from name '");
        Log(pr->name);
        Log("'\r\n");
    }
    DnsQueryIp4FromName(pr->name, pr->protocol);
}
static void sendRequest(struct record* pr)
{
    if ( DnsQueryIsBusy         ) return;
    if ( pr->state != STATE_WANT) return;
    if (!pr->protocol           ) return;
    
    if (pr->todo == TODO_NAME_FROM_IP) queryNameFromIp(pr);
    if (pr->todo == TODO_IP_FROM_NAME) queryIpFromName(pr);
    
    pr->state = STATE_SENT;
    pr->elapsed = MsTimerCount;
}
void Nr4Main()
{
    static int i = -1;
    i++;
    if (i >= RECORDS_COUNT) i = 0;
    
    struct record* pr = &records[i];
    
    clearCache  (pr);
    nextProtocol(pr);
    sendRequest (pr);
}
void Nr4Init()
{
    for (int i = 0; i < RECORDS_COUNT; i++) records[i].state = STATE_EMPTY;
}