Thursday, August 20, 2009

Arduino Web Server

Hacking means find the right way to compromise a system, often software is not enough and sometime the attacker needs a hardware trick to compromise the whole system. This is the reason why I'm studying the Arduino board. Some of the real projects that I'm doing cannot be published (so far), but some else can .
Using a simple Arduino 2009, an Ethernet shield and a common LCD is possible to make an interesting small and portable web-server. Following the code with a short descriptions.



What you need is to download and to install (/hardware/library/) the following libraries:


/****************************************************/
// Written by Marco Ramilli
#include //Ethernet Library by Arduino
#include "Dhcp.h" //DhCP Library
#include //Default String Library
#include // String Library, you have to download it from Arduino web-site
#include // Second String Library, you have to download it from Arduino web-site
#include //Liquid Crystal Output
/****************************************************/


Note1: I've used DHCP library even if a web server should have a static IP ... ...
Note2: I've divided everything in classes, in such way you can divide the code in multiple files.


//defining Pages CLass
class Pages
{
public:
Pages(char *header, char *pageStart, char *body, char *pageEnd) { _header = header; _pageStart = pageStart; _body = body; _pageEnd = pageEnd; }
char *getHeader(){return _header;}
char *getPageStart(){return _pageStart;}
char *getBody(){return _body;}
char *getPageEnd(){return _pageEnd;}
char *getPage(){strcat(_header,_pageStart); strcat(_header,_body); strcat(_header,_pageEnd); return _header; }
void appendToBody(char *stringToAppend){strcat(_body,stringToAppend); }

private:
char *_header;
char *_pageStart;
char *_body;
char *_pageEnd;
};
/****************************************************/
// defining Networkconfig Class
class NetworkConfig
{
public:
NetworkConfig(byte *mac, byte *ip){_mac = mac; _ip = ip;}
NetworkConfig(byte *mac, byte *ip, byte *mask, byte *gw, byte *dhcps, byte *dns) {_mac = mac; _ip = ip; _mask = mask; _gw = gw; _dhcps = dhcps; _dns = dns; }
void setIP(byte *ip){_ip = ip;}
void setMAC(byte *mac){_mac = mac;}
void setMASK(byte *mask){_mask = mask;}
void setGW(byte *gw){_gw = gw;}
void setDHCPS(byte *dhcps){_dhcps = dhcps;}
void setDNS(byte *dns){_dns = dns;}

byte *getIP(){return _ip;}
byte *getMAC(){return _mac;}
byte *getMASK(){return _mask;}
byte *getGW(){return _gw;}
byte *getDHCPS(){return _dhcps;}
byte *getDNS(){return _dns;}
private:
byte *_ip;
byte *_mac;
byte *_mask;
byte *_gw;
byte *_dhcps;
byte *_dns;
};



Of curse, if no DHCP is detected, the server wanna be on 192.168.1.50 .


/****************************************************/
//Defult Network Configuration instances
byte ip[] = { 192, 168, 1, 50 };
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
NetworkConfig defaultconf(mac, ip);
/****************************************************/



Here some useful cost to define.


/*****************************************************/
#define maxLength 25 // preventing BoF
#define serverPort 80 // listening port
String inString = String(maxLength);
Server server(serverPort);
int val, p;
LiquidCrystal lcd(8, 7, 6, 5, 4, 3, 2); //Declare a LDC display, for ping look over
/****************************************************/



Here the real interesting code... ...


void setup() // before going on, we need some stuff
{
Serial.begin(9600); // just for debugginh
lcd.begin(1,8); // configure LCD display size (raw, col)
if(!getNC()){
Ethernet.begin(defaultconf.getMAC(), defaultconf.getIP() );
}
server.begin();
}
/****************************************************/
void loop()
{
Client client = server.available(); //Gets a client that is connected to the server and has data available for reading
if (client) {
boolean current_line_is_blank = true; // html protocol ....
while (client.connected()) {

if (client.available()) { // client is sending something
p = 0; //lets see if the client wants initial page
char c = client.read(); //reading whatever the client sends
if (inString.length() < maxLength) { // currently there is no way to make it dinamic
inString.append(c);
Serial.println(inString); //Only To DEBUG !
}
if (c == '\n' && current_line_is_blank) { //means the client has stopped to end us chars
if (inString.contains("?")) { // means the client wanna communicate with us
int Pos_p = inString.indexOf("p");
p = atoi(inString.substring((Pos_p+2), (Pos_p+4)));
}




This case is useful to figure out POST and/or GET actions.


switch (p){
// due to BoF bug, actually is not possible to write index.getPage(). But late versions of Arduino ... ... ...
case 0:
printonlcd("Index");
client.println(index.getHeader() );
client.println(index.getPageStart() );
client.println(index.getBody() );
client.println(index.getPageEnd() );
break;

case 1:
printonlcd("Page_1");
client.println(page1.getHeader() );
client.println(page1.getPageStart() );
client.println(page1.getBody() );
client.println(page1.getPageEnd() );
break;
case 2:
printonlcd("Page_2");
client.println(page2.getHeader() );
client.println(page2.getPageStart() );
client.println(page2.getBody() );
client.println(page2.getPageEnd() );
break;
default:
printonlcd("Page_D");
client.println(index.getHeader() );
client.println(index.getPageStart() );
client.println(index.getBody() );
client.println(index.getPageEnd() );
break;
}

break;

}
if (c == '\n') {
current_line_is_blank = true;
} else if (c != '\r') {
current_line_is_blank = false;
}
}
}
delay(1);
inString = "";
client.stop();
}
}



Here how dchp library works, thank to Jordan Terrel



/****************************************************/
//DHCP client
int getNC(){
int result = Dhcp.beginWithDHCP(mac);
if (result == 1){
byte buffer[6];
Dhcp.getLocalIp(buffer);
Serial.print("ip address: ");
printArray(&Serial, ".", buffer, 4, 10);
Dhcp.getSubnetMask(buffer);
Serial.print("subnet mask: ");
printArray(&Serial, ".", buffer, 4, 10);
Dhcp.getGatewayIp(buffer);
Serial.print("gateway ip: ");
printArray(&Serial, ".", buffer, 4, 10);
Dhcp.getDhcpServerIp(buffer);
Serial.print("dhcp server ip: ");
printArray(&Serial, ".", buffer, 4, 10);
Dhcp.getDnsServerIp(buffer);
Serial.print("dns server ip: ");
printArray(&Serial, ".", buffer, 4, 10);
Serial.print("READY");
printonlcd("READY");
return 1;
}else{
Serial.print("No DHCP, Running in default conf");
printonlcd("DEFAULT");
return 0;
}
}



Here how LCD library works



/****************************************************/

void printonlcd(String text){
lcd.clear();

if (text.length() <= 7){
lcd.print(text);
lcd.blink();


}else{ // n with this lcd
int index =0;
while (index >=0){
delay(200);
for (int positionl = 0; positionl < 8; positionl ++){
lcd.scrollDisplayLeft();
delay(150);
}
for (int positionr = 0; positionr < 8; positionr ++){
lcd.scrollDisplayRight();
delay(150);
}
index--;
}
}
}

//printArray funciton
void printArray(Print *output, char* delimeter, byte* data, int len, int base){
char buf[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
for(int i = 0; i < len; i++)
{
if(i != 0)
output->print(delimeter);
output->print(itoa(data[i], buf, base));
}
output->println();
}






That's all !
Hope to be useful .

1 comment:

Cris said...

Salve Prof!
Si ricorda di me? Sono Cristian Capannini, ho partecipato alle sue lezioni al corso IFTS. :D. Come va?
Adesso lavoro a "IL FABBRO DEL COMPUTER!". ;)