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/dnsname.cpp
- Committer:
- andrewboyson
- Date:
- 2017-07-03
- Revision:
- 22:914b970356f0
- Parent:
- 13:9cd54f7db57a
- Child:
- 23:b641979389b2
File content as of revision 22:914b970356f0:
#include "mbed.h" #include "dnshdr.h" #include "ctype.h" #include "log.h" #define DEBUG false #define MAX_PACKET_SIZE 512 #define MAX_DEPTH 10 static bool isOffset(char c) { return (c & 0xC0) == 0xC0; } static char* derefOffset(char* p) { int offset = (*p & 0x3F) << 8; //Any bits in the upper byte are added p++; //Move to the lower byte offset |= *p; //And add it return DnsHdrPacket + offset; //Return a pointer } int DnsNameLength(char* pStart) //Returns 0 on error { char* p = pStart; while (true) { if (p > DnsHdrPacket + MAX_PACKET_SIZE) { if (DEBUG) LogTimeF("DnsNameLength strayed out of the packet\r\n"); return 0; } int length = *p++; if (length == 0) //Its the last field so finish { return p - pStart; } else if (isOffset(length)) //it's a pointer so skip the length byte and return { p++; return p - pStart; } else //it's a length plus a field eg 3www { p += length; } } } bool DnsNameCompare(char* pStart, char* pTarget) { char* p = pStart; bool isSame = true; int depth = 0; while (true) { if (p > DnsHdrPacket + MAX_PACKET_SIZE) { if (DEBUG) LogTimeF("DnsNameCompare strayed out of the packet\r\n"); return false; } int length = *p; if (length == 0) //Its the last field so should be on the target's terminating NUL { if (*pTarget) isSame = false; return isSame; } else if (isOffset(length)) //it's a pointer so go up one { depth++; if (depth > MAX_DEPTH) { if (DEBUG) LogTimeF("DnsNameCompare exceeded depth limit\r\n"); return false; } p = derefOffset(p); } else //it's a length plus a field eg 3www { if (p > pStart) if (*pTarget++ != '.') isSame = false; p++; for (int i = 0; i < length; i++) { if (toupper(*pTarget++) != toupper(*p++)) isSame = false; } } } } int DnsNameIndexFromPointer(char* p) { int depth = 0; while (isOffset(*p)) //it's a pointer so jump to the new offset eg ^C0 ^0C { depth++; if (depth > MAX_DEPTH) { if (DEBUG) LogTimeF("DnsNameIndexFromPointer exceeded depth limit\r\n"); return -1; } p = derefOffset(p); } return p - DnsHdrPacket; } void DnsNameDecode(int offset, int lenName, char* pName) { bool nameStarted = false; int depth = 0; char* p = DnsHdrPacket + offset; while (true) { if (p > DnsHdrPacket + MAX_PACKET_SIZE) { *pName = 0; LogTimeF("DnsNameDecode could not find name end\r\n"); return; } int length = *p; if (length == 0) //Its the last field so terminate the string { *pName = 0; return; } else if (isOffset(*p)) //it's a pointer so jump to the new offset eg ^C0 ^0C { depth++; if (depth > MAX_DEPTH) { LogTimeF("DnsNameDecode exceeded depth limit\r\n"); *pName = 0; return; } p = derefOffset(p); } else //it's a length plus a field eg 3www { if (pName) { if (length + 1 > lenName) //Leave room to terminate the string { *pName = 0; LogTimeF("DnsNameDecode overran name buffer\r\n"); return; } lenName -= length + 1; //name chunk plus a '.' if (nameStarted) *pName++ = '.'; p++; for (int i = 0; i < length; i++) *pName++ = *p++; nameStarted = true; } else { p += length + 1; } } } } void DnsNameDecodeIp4(int offset, uint32_t* pIp) { int field = 0; int depth = 0; char* p = DnsHdrPacket + offset; while (true) { if (p > DnsHdrPacket + MAX_PACKET_SIZE) { *pIp = 0; LogTimeF("DnsNameDecodeIp4 could not find name end\r\n"); return; } int length = *p; if (length == 0) //Its the last field so return the ip if it is the correct length { if (field != 6) *pIp = 0; return; } else if (isOffset(*p)) //it's a pointer so jump to the new offset eg ^C0 ^0C { depth++; if (depth > MAX_DEPTH) { LogTimeF("DnsNameDecodeIp4 exceeded depth limit\r\n"); *pIp = 0; return; } p = derefOffset(p); } else //it's a length plus a field eg 3www { p++; // move past the length if (field <= 3) { if (length > 3) {*pIp = 0; return; } // expect 90.1.168.192.in-addr.arpa int byte = 0; for (int i = 0; i < length; i++) { byte = byte * 10; if (*p > '9' || *p < '0') {*pIp = 0; return; } // expect 90.1.168.192.in-addr.arpa byte += *p - '0'; p++; } *pIp <<= 8; *pIp |= byte; } else if (field == 4) { if (length != 7) {*pIp = 0; return; } // expect 90.1.168.192.in-addr.arpa p+= length; } else if (field == 5) { if (length != 4) {*pIp = 0; return; } // expect 90.1.168.192.in-addr.arpa p+= length; } else { *pIp = 0; return; // expect 90.1.168.192.in-addr.arpa } field++; } } } void DnsNameDecodeIp6(int offset, char* pIp) { int field = 0; int depth = 0; char* p = DnsHdrPacket + offset; while (true) { if (p > DnsHdrPacket + MAX_PACKET_SIZE) { pIp[0] = 0; LogTimeF("DnsNameDecodeIp6 could not find name end\r\n"); return; } int length = *p; if (length == 0) //Its the last field so return the ip if it is the correct length { if (field != 34) pIp[0] = 0; return; } else if (isOffset(*p)) //it's a pointer so jump to the new offset eg ^C0 ^0C { depth++; if (depth > MAX_DEPTH) { LogTimeF("DnsNameDecodeIp6 exceeded depth limit\r\n"); *pIp = 0; return; } p = derefOffset(p); } else //it's a length plus a field eg 3www { p++; // move past the length if (field <= 31) { if (length > 1) {pIp[0] = 0; return; } // expect 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 int nibble = 0; if (*p >= '0' && *p <= '9') nibble = *p - '0'; else if (*p >= 'a' && *p <= 'f') nibble = *p - 'a' + 10; else if (*p >= 'A' && *p <= 'F') nibble = *p - 'A' + 10; else {pIp[0] = 0; return; } // expect 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 if ((field & 1) == 0) { pIp[15 - (field >> 1)] = nibble; } else { nibble <<= 4; pIp[15 - (field >> 1)] |= nibble; } p += length; } else if (field == 32) { if (length != 3) {pIp[0] = 0; return; } // expect 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+= length; } else if (field == 33) { if (length != 4) {pIp[0] = 0; return; } // expect 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+= length; } else { pIp[0] = 0; return; // expect 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 } field++; } } } void DnsNameEncodeIp4(uint32_t ip, char** pp) { char* p = *pp; //Get a convenient pointer to the next byte char* pLen = p; p++; *pLen = sprintf(p, "%d", (ip & 0xff000000) >> 24); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "%d", (ip & 0x00ff0000) >> 16); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "%d", (ip & 0x0000ff00) >> 8); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "%d", (ip & 0x000000ff) >> 0); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "in-addr"); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "arpa"); p += *pLen; pLen = p; p++; *pLen = 0; *pp = p; //Save the convenient pointer back into the pointer to pointer } void DnsNameEncodeIp6(char* ip, char** pp) { char* p = *pp; //Get a convenient pointer to the next byte ip += 16; for (int i = 0; i < 16; i++) { ip--; int v; *p++ = 1; v = *ip & 0x0F; *p++ = v < 10 ? v + '0' : v - 10 + 'a'; *p++ = 1; v = *ip >> 4; *p++ = v < 10 ? v + '0' : v - 10 + 'a'; } char* pLen = p; p++; *pLen = sprintf(p, "ip6"); p += *pLen; pLen = p; p++; *pLen = sprintf(p, "arpa"); p += *pLen; pLen = p; p++; *pLen = 0; *pp = p; //Save the convenient pointer back into the pointer to pointer } void DnsNameEncode(char* pName, char** pp) { char* p = *pp; //Get a convenient pointer to the next byte char* pLen = p; //Record the position of the first length byte p++; //Move to the start of the first field while (true) { char c = *pName; switch (c) { case 0: *pLen = p - pLen - 1; *p = 0; p++; *pp = p; //Save the convenient pointer back into the pointer to pointer return; case '.': *pLen = p - pLen - 1; pLen = p; p++; break; default: *p = c; p++; break; } pName++; } }