Skip to content

Instantly share code, notes, and snippets.

@laclefyoshi
Created January 22, 2011 03:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laclefyoshi/790835 to your computer and use it in GitHub Desktop.
Save laclefyoshi/790835 to your computer and use it in GitHub Desktop.
DNS server like dnsmasq with Arduino and EthernetShield
// -*- mode: c++ -*-
/**
Copyright: (c) SAEKI Yoshiyasu
License : MIT-style license
<http://www.opensource.org/licenses/mit-license.php>
last updated: 2011/01/21
**/
#include <SPI.h>
#include <Ethernet.h>
#include <Udp.h>
#include <SD.h>
// setting for EthernetShield
byte mac[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
byte ip[] = {192, 168, 254, 100};
const int sdSelect = 4;
char hostsPath[] = "/etc/hosts";
unsigned int listenPort = 53;
byte remoteIp[4];
unsigned int remotePort;
#define PACKET_MAX_SIZE 128
char requestBuffer[PACKET_MAX_SIZE];
char responseBuffer[PACKET_MAX_SIZE];
char reqDomain[40];
byte resIp[4];
struct ADPair {
char domain[40];
byte ipaddr[4];
};
#define MAX_PAIR_SIZE 7
struct ADPair pairs[MAX_PAIR_SIZE];
int pairIdx = 0;
void setup() {
Ethernet.begin(mac, ip);
Udp.begin(listenPort);
// Serial.begin(9600);
// Serial.print("Init SD card...");
if (!SD.begin(sdSelect)) {
// Serial.println("SD Card failed");
return;
}
// Serial.println("done.");
readHosts();
}
void loop() {
int requestSize = Udp.available();
if(requestSize) {
Udp.readPacket(requestBuffer, PACKET_MAX_SIZE, remoteIp, remotePort);
int type = (requestBuffer[2] >> 3) & 15;
if(type == 0) { // nomal request
getReqDomain();
if(reqDomain[0] != '\0') { // request exists
setResIp();
int responseSize = generateResponseData(requestSize);
Udp.sendPacket((uint8_t *)responseBuffer, (uint16_t)(responseSize - 1),
remoteIp, remotePort);
}
}
}
}
void readHosts() {
File hostsFile = SD.open(hostsPath);
if (hostsFile) {
char line[90];
char domain[40];
char alias[30];
byte ipaddr[4];
while (hostsFile.available()) {
int idx = 0;
char c;
do {
c = hostsFile.read();
if(c == '#') {
while(c != '\n') {
c = hostsFile.read();
}
break;
}
line[idx++] = c;
} while(c != '\n');
line[idx] = '\0';
if(sscanf(line, "%d.%d.%d.%d %s %s",
&ipaddr[0], &ipaddr[1], &ipaddr[2], &ipaddr[3], domain, alias) == 6) {
strcpy(pairs[pairIdx].domain, domain);
for(int j = 0; j < 4; j++) {
pairs[pairIdx].ipaddr[j] = ipaddr[j];
}
pairIdx++;
strcpy(pairs[pairIdx].domain, alias);
for(int j = 0; j < 4; j++) {
pairs[pairIdx].ipaddr[j] = ipaddr[j];
}
pairIdx++;
} else if(sscanf(line, "%d.%d.%d.%d %s",
&ipaddr[0], &ipaddr[1], &ipaddr[2], &ipaddr[3], domain) == 5) {
strcpy(pairs[pairIdx].domain, domain);
for(int j = 0; j < 4; j++) {
pairs[pairIdx].ipaddr[j] = ipaddr[j];
}
pairIdx++;
}
if(pairIdx >= MAX_PAIR_SIZE) {
break;
}
}
free(line);
free(domain);
free(alias);
free(ipaddr);
} else {
// Serial.println("error: open /etc/hosts");
}
hostsFile.close();
// Serial.print("entries: "); Serial.println(pairIdx);
}
void getReqDomain() {
int ini = 12;
int lon = requestBuffer[ini];
int i = 0;
while(lon != 0) {
for(int j = 0; j < lon; j++) {
reqDomain[i++] = requestBuffer[ini + j + 1];
}
reqDomain[i++] = '.';
ini += lon + 1;
lon = requestBuffer[ini];
}
reqDomain[i] = '\0';
// Serial.println(reqDomain);
}
void setResIp() {
for(int t = 0; t < 4; t++) {
resIp[t] = 0;
}
reqDomain[strlen(reqDomain) - 1] = '\0';
for(int pI = 0; pI < pairIdx; pI++) {
if(strcmp(pairs[pI].domain, reqDomain) == 0) {
for(int t = 0; t < 4; t++) {
resIp[t] = pairs[pI].ipaddr[t];
}
}
}
}
int generateResponseData(int requestSize){
// RFC 1035
int resIndex = 0;
for(int k = 0; k < 2; k++) { // identification
responseBuffer[resIndex++] = requestBuffer[k];
}
if(resIp[0] == 0) { // no name
responseBuffer[resIndex++] = '\x81'; // normal server
responseBuffer[resIndex++] = '\x02'; // server failure
for(int k = 4; k < 6; k++) { // question
responseBuffer[resIndex++] = requestBuffer[k];
}
for(int k = 4; k < 6; k++) { // answer
responseBuffer[resIndex++] = '\x00';
}
for(int k = 0; k < 4; k++) { // authority, addition
responseBuffer[resIndex++] = '\x00';
}
} else { // name exists
responseBuffer[resIndex++] = '\x85'; // normal server
// authoritative
responseBuffer[resIndex++] = '\x80'; // recursive
// no error
for(int k = 4; k < 6; k++) { // question
responseBuffer[resIndex++] = requestBuffer[k];
}
for(int k = 4; k < 6; k++) { // answer
responseBuffer[resIndex++] = requestBuffer[k];
}
for(int k = 0; k < 4; k++) { // authority, addition
responseBuffer[resIndex++] = '\x00';
}
for(int k = 12; k < requestSize - 8; k++) { // question
responseBuffer[resIndex++] = requestBuffer[k];
}
responseBuffer[resIndex++] = '\xc0';
responseBuffer[resIndex++] = '\x0c';
responseBuffer[resIndex++] = '\x00'; // type
responseBuffer[resIndex++] = '\x01';
responseBuffer[resIndex++] = '\x00'; // class
responseBuffer[resIndex++] = '\x01';
responseBuffer[resIndex++] = '\x00'; // ttl
responseBuffer[resIndex++] = '\x00';
responseBuffer[resIndex++] = '\x00';
responseBuffer[resIndex++] = '\x3c';
responseBuffer[resIndex++] = '\x00'; // ip length
responseBuffer[resIndex++] = '\x04';
responseBuffer[resIndex++] = resIp[0]; // ip
responseBuffer[resIndex++] = resIp[1];
responseBuffer[resIndex++] = resIp[2];
responseBuffer[resIndex++] = resIp[3];
}
responseBuffer[resIndex++] = '\x00'; // end
return resIndex;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment