Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: oldheating gps motorhome heating
resolve/nr4.c
- Committer:
- andrewboyson
- Date:
- 2019-03-11
- Revision:
- 128:79052cb4a41c
- Parent:
- 116:60521b29e4c9
- Child:
- 132:db2174b36a6d
File content as of revision 128:79052cb4a41c:
#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 (!MsTimerHasElapsed(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 (!MsTimerHasElapsed(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);
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 (MsTimerHasElapsed(pr->elapsed, CACHE_TIMEOUT_MS)) pr->state = STATE_EMPTY;
}
static void nextProtocol(struct record* pr)
{
if (pr->state == STATE_SENT && MsTimerHasElapsed(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;
}