Andrew Boyson / net

Dependents:   oldheating gps motorhome heating

Revision:
61:aad055f1b0d1
Parent:
60:1d8c7a1e7483
Child:
65:37acccf2752f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eth/nr4.c	Thu Jan 11 17:38:21 2018 +0000
@@ -0,0 +1,375 @@
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include      "log.h"
+#include    "clock.h"
+#include      "net.h"
+#include      "mac.h"
+#include  "ip4addr.h"
+#include      "dns.h"
+#include     "dhcp.h"
+#include "dnsquery.h"
+#include     "http.h"
+
+bool Nr4Trace = false;
+
+#define NAME_MAX_LENGTH        20
+#define CACHE_TIMEOUT        3600
+#define FREEZE_TIMEOUT       1800
+#define REPLY_TIMEOUT           2
+
+#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
+
+static uint32_t elapsed = 0;
+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 (DnsHostNamesEquate(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 (DnsHostNamesEquate(records[i].name, name)) return i;
+    }
+    return -1;
+}
+static int getOldest()
+{
+    int iN = 0;
+    uint32_t tN = 0xFFFFFFFF;
+    for (int i = 0; i < RECORDS_COUNT; i++)
+    {
+        if (records[i].state == STATE_EMPTY) return i; //Found an empty slot so just return it
+        if (records[i].elapsed < tN)
+        {
+            tN = records[i].elapsed;
+            iN = i;
+        }
+    }  
+    return iN;                         //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 (elapsed < records[i].elapsed + FREEZE_TIMEOUT) 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  = elapsed;
+        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  = elapsed;
+    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 (elapsed < records[i].elapsed + FREEZE_TIMEOUT) 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  = elapsed;
+        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  = elapsed;
+    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  = elapsed;
+    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 (DnsHostNamesEquate(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)
+{
+    int newest = 0;
+    *pIp = 0;
+    for (int i = 0; i < RECORDS_COUNT; i++)
+    {
+        if (records[i].state == STATE_EMPTY) continue;
+        if (!records[i].ip) continue;
+        if (!DnsHostNamesEquate(records[i].name, name)) continue;
+        if (records[i].elapsed > newest)
+        {
+            newest = records[i].elapsed;
+            *pIp = records[i].ip;
+        }
+    }  
+}
+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 ", (elapsed - records[i].elapsed) / 60);
+            
+            int ipLen = Ip4AddressHttp(records[i].ip);
+            HttpFillChar(' ', 40 - ipLen);
+                        
+            HttpAddChar(letterFromStateAndProtocol(records[i].state, records[i].protocol));
+            
+            HttpAddChar(' ');
+            
+            HttpAddText(records[i].name);
+            
+            HttpAddChar('\r');
+            HttpAddChar('\n');
+        }
+    }
+}
+static void clearCache(struct record* pr)
+{
+    if (elapsed > pr->elapsed  + CACHE_TIMEOUT) pr->state = STATE_EMPTY;
+}
+static void nextProtocol(struct record* pr)
+{
+    if (pr->state == STATE_SENT && elapsed > pr->elapsed + REPLY_TIMEOUT && 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 = elapsed;
+    }
+}
+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 = elapsed;
+}
+void Nr4Main()
+{
+    static int i = -1;
+    i++;
+    if (i >= RECORDS_COUNT) i = 0;
+    
+    struct record* pr = &records[i];
+    
+    clearCache  (pr);
+    nextProtocol(pr);
+    sendRequest (pr);
+    
+    if (ClockTicked) elapsed++;
+}
+void Nr4Init()
+{
+    for (int i = 0; i < RECORDS_COUNT; i++) records[i].state = STATE_EMPTY;
+}