Go Down

Topic: Detect ip of client? (Read 1 time) previous topic - next topic

ematson5897

I have a Mega 2560 and an Ethernet shield, and basically I'm prototyping an architectural lighting controller for my church that is controlled via a web interface. I would like to be able to have it password protected, I.e. client is presented with a text box and enters password before they are able to control anything. So I would need to be able to identify each client to see if they have logged on already or not. So is there a way to find a clients IP, or individually identify it?

SurferTim

Here is a thread that covers what you want.
http://arduino.cc/forum/index.php/topic,135082.0.html

pylon

Why not using HTTP authentication and checking the client's password at every request?

ematson5897

Yea I'm actually one step ahead of you on that. But how do you log the client out?

pylon

Define "log out". Do you want to deny access after some time? If you send the client a 4XX result code it will ask again for the credentials if that's what you want.

ematson5897

#5
Feb 06, 2013, 03:16 pm Last Edit: Feb 06, 2013, 03:33 pm by ematson5897 Reason: 1
Well I had issues with browsers eternally remembering the password and stuff but I fixed it. I have gotten it where it has a list of logins on an SD card with permissions that it compares it to. So all I have to do now is change the page presented and stuff

pylon

Usually the browser remembers the credentials during runtime. If you quit the browser, it should forget all credentials the user not explicitly saved (and saving is possible for web forms too).
If you wanna circumvent that, send a random realm, because the browser stores the credentials for one realm and re-ask for them if the realm changes.

ematson5897

#7
Feb 06, 2013, 09:53 pm Last Edit: Feb 06, 2013, 10:16 pm by ematson5897 Reason: 1
Ok. Heres my code so far:
Code: [Select]
#include <Base64.h>
#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#include <LiquidCrystal.h>
const byte slots = 20;
IPAddress clientIP[slots];
boolean IPUsed[slots];
boolean passEntered[slots];
int currClient;
boolean newClient;
int loginCount[slots];
char encoded[100];
char decoded[100];
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 6, 3, 2);
File accounts;
const char userHeader[] = "<user>";
const char userFooter[] = "</user>";
char permissions[slots];

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };

EthernetServer server(80);

void setup()
{
   Serial.begin(115200);    
 pinMode(53, OUTPUT);     // change this to 53 on a mega
 pinMode(10, OUTPUT);     // change this to 53 on a mega
 digitalWrite(10,HIGH);
 Serial.print("Initializing SD card...");
 
 if (!SD.begin(4)) {
   Serial.println("initialization failed!");
   return;
 }
 Serial.println("initialization done.");
 lcd.begin(16, 2);
 pinMode(8, OUTPUT);    
 Ethernet.begin(mac);
 server.begin();
 
 
   lcd.print("IP:");

 for (byte thisByte = 0; thisByte < 4; thisByte++) {
   // print the value of each byte of the IP address:
   lcd.print(Ethernet.localIP()[thisByte], DEC);
   
   if(thisByte != 3)
     lcd.print(".");
 }
}

void loop()
{
 
 EthernetClient client = server.available();
 if (client) {
   IPAddress temp;
   temp = client.remoteIP();
   Serial.println(temp);
   
   boolean matched = false;
   for(int i = 0; i < slots; i++)
   {
       if(temp == clientIP[i])
       {
         Serial.println("returning");
         newClient = false;
         loginCount[i] ++;
         matched = true;
         currClient = i;

       }
   }
   
   if(!matched)
   {
     for(int i = 0; i < slots; i++)
     {
       if(!IPUsed[i])
       {
         Serial.println("new");
         newClient = true;
         loginCount[i] = 0;
         clientIP[i] = temp;
         IPUsed[i] = true;
         currClient = i;
         passEntered[i] = false;
         return;
       }
     }
     
   }
   
   lcd.setCursor(0,1);
   lcd.print('C');
   lcd.print(currClient);
   lcd.print(':');
   lcd.println(clientIP[currClient]);
   boolean currentLineIsBlank = true;
   String buffer = "";  
   while (client.connected()) {
     if (client.available()) {
       char c = client.read();
       Serial.print(c);  
       buffer+=c;      
       if (c == '\n' && currentLineIsBlank) {
        if(passEntered[currClient])
        {
          drawUser(client);
         
        }
        else
        {
         drawPass(client);
        }
        break;
       }
       if (c == '\n') {
         
         currentLineIsBlank = true;
         buffer="";      
       } else if (c == '\r') {  
         
         if((loginCount[currClient] > 1) && (buffer.indexOf("Authorization: Basic ") >= 0))
         {            
           for(int i = buffer.indexOf("Authorization: Basic ")+21; i < buffer.length()-1; i++)
           {
             encoded[i-(buffer.indexOf("Authorization: Basic ")+21)] = buffer[i];
           }
           
           base64_decode(decoded , encoded , buffer.length() - 21 - 1);
           
           accounts = SD.open("accounts.txt");
           if (accounts)
           {
             Serial.print("reading accounts.txt...");
             int userStart = -1;
             int userEnd = -1;
             boolean failed = false;
            while(!passEntered[currClient] && !failed )
            {
             boolean found = false;
             unsigned long i = userStart + 1;
             while(!found)
             {
               if(!accounts.seek(i))
               {
                 found = true;
                 failed = true;
               }
               char buf[10];
               accounts.read(buf, 6);      
               if(strncmp(buf, userHeader, 6) == 0)
               {
                   userStart = i;
                   found = true;
               }
               i++;
             }
             found = false;
             i = userEnd + 1;
             while(!found)
             {
               if(!accounts.seek(i))
               {
                 found = true;
                 failed = true;
               }
               char buf[10];
               accounts.read(buf, 7);      
               if(strncmp(buf, userFooter, 7) == 0)
               {
                   userEnd = i;
                   found = true;
               }
               i++;
             }
             if(!failed)
             {
               accounts.seek(userStart + 6);
               passEntered[currClient] = true;
               i = 0;
               while(accounts.position() != userEnd-2)
               {
                 Serial.print(accounts.peek());
                 if(decoded[i] != accounts.read())
                 {
                   passEntered[currClient] = false;
                 }
                 i++;
               }
               if(passEntered[currClient])
               {
                 int tempPos = accounts.position();
                 accounts.seek(userEnd - 1);
                 permissions[currClient] = accounts.read();
                 accounts.seek(tempPos);
               }
             }




            }
             accounts.close();
           
           } else {
             // if the file didn't open, print an error:
             Serial.println("error opening test.txt");
           }

           
           
           
         }
         
         if( passEntered[currClient])
         {
           
           if(buffer.indexOf("GET /?status=1")>=0)
             digitalWrite(8,HIGH);  
           
           if(buffer.indexOf("GET /?status=0")>=0)
             digitalWrite(8,LOW);  
         }
       }
       else {
         
         currentLineIsBlank = false;
       }
     }
   }
   
   client.stop();
 }
}

void drawUser(EthernetClient client)
{
 client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
         client.println();
         

           client.print("<BODY BGCOLOR=#000000 TEXT=#FFFFFF LINK=#FFFFFF VLINK=#FFFFFF>");
           
           client.print("<font color='blue' size=7>Welcome  ");
           
           if(permissions[currClient] == 'u')
             client.print("user ");
             
           if(permissions[currClient] == 'a')
             client.print("admin ");
             
             
         
         
         client.print(currClient);
         
         client.print(" </font>");
         
         client.println("<br />");
         client.println("<br />");
         
         if (digitalRead(8)){  
           client.print(" LED is <font color='green'>ON</font>");
         }else{
           client.print(" LED is <font color='red'>OFF</font>");
         }
         client.println("<br />");
         
         client.print("<FORM action=\"http://");
         client.print(Ethernet.localIP()[0]);
         client.print(".");
         client.print(Ethernet.localIP()[1]);
         client.print(".");
         client.print(Ethernet.localIP()[2]);
         client.print(".");
         client.print(Ethernet.localIP()[3]);
         client.print("/\" >");
         
         client.print("<P> <INPUT type=\"radio\" name=\"status\" value=\"1\">ON");
         client.print("<P> <INPUT type=\"radio\" name=\"status\" value=\"0\">OFF");
         client.print("<P> <INPUT type=\"submit\" value=\"Submit\"> ");
         client.print("</FORM>");
         
}

void drawPass(EthernetClient client)
{
            client.println("HTTP/1.1 401 Access Denied");
         client.println("WWW-Authenticate: Basic realm=\"a\"");
         
         client.println("Content-Type: text/html");
         client.println();
         

           client.print("<BODY BGCOLOR=#000000 TEXT=#FFFFFF LINK=#FFFFFF VLINK=#FFFFFF>");
           client.print("ERROR: ACCESS DENIED");
}



Basically it is a password protected led on pin 8. all of the users are stored on a SD card in a file "accounts.txt" with the format of:
<user>username:password:p</user> where p is a for admin or u for user

pylon

Code: [Select]
    String buffer = ""; 


Unfortunately using the String class is a very bad idea. It fragments your available RAM in no time, especially if used the way you use it. Additionally there's a bug in the freeing of dynamically allocated memory in the current IDE versions which makes using the String class even worse. If you don't want to be forced to reset your Arduino every few requests you should eliminate the String class from your project.

ematson5897

#9
Feb 07, 2013, 09:01 pm Last Edit: Feb 07, 2013, 10:21 pm by ematson5897 Reason: 1
Ok. I guess I need to whip up a function to replicate indexOf in a char array


EDIT:
Got it. Now for implementation
Code: [Select]
int findStr(char * strA, char * strB)
{
  int location = strstr(strA , strB)-strA+1;
  if(location<0)
  {
    return -1;
  }
  return location;
}

zoomkat

Quote
If you don't want to be forced to reset your Arduino every few requests you should eliminate the String class from your project.


I've got some simple server code that uses the String class and had the server respond to a client request 500,000+ times with out a crash. Perhaps your code is defective.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

pylon

Quote
I've got some simple server code that uses the String class and had the server respond to a client request 500,000+ times with out a crash. Perhaps your code is defective.


If you are very cautious and know where the class is defective for en embedded environment, this is possible. But generally it's probably the worst decision of the Arduino makers to have this class included in the IDE. The way the OP uses the class he won't be able to answer 500'000 requests. And he does nothing the documentation says you shouldn't do.

zoomkat

#12
Feb 08, 2013, 05:54 pm Last Edit: Feb 08, 2013, 05:56 pm by zoomkat Reason: 1

Quote
I've got some simple server code that uses the String class and had the server respond to a client request 500,000+ times with out a crash. Perhaps your code is defective.


If you are very cautious and know where the class is defective for en embedded environment, this is possible. But generally it's probably the worst decision of the Arduino makers to have this class included in the IDE. The way the OP uses the class he won't be able to answer 500'000 requests. And he does nothing the documentation says you shouldn't do.


Well, guess we will see if the deletion of the String class fixes the OP's issue. Specifically which String useage in the OP's origional posted code is causing his issue? Did the String memory location "go out of scope" in the OP's code? The issue you pointed to (below) may well fix any "bug" issue by emptying the memory location such that there is no longer memory that needs to be freeded in that location. I read closely the anti String posting and don't remember ever actually seeing an issue being fixed by the removal of String useage.  I'll observe to see how this plays out.

Code: [Select]
   String buffer = "";  
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

pylon

Quote
I read closely the anti String posting and don't remember ever actually seeing an issue being fixed by the removal of String useage.


I had several threads where the change from the String class to using character arrays fixed the problems.

Anyway, I didn't say that the line I posted was posing problems for the OP, that was just the indication that he uses the String class. Because I've seen lots of strange behaviors because of the usage of the String class, that's always the first thing I try to eliminate.

zoomkat


Quote
I read closely the anti String posting and don't remember ever actually seeing an issue being fixed by the removal of String useage.


I had several threads where the change from the String class to using character arrays fixed the problems.

Anyway, I didn't say that the line I posted was posing problems for the OP, that was just the indication that he uses the String class. Because I've seen lots of strange behaviors because of the usage of the String class, that's always the first thing I try to eliminate.


Also DO NOT FLY ON AIRPLANES BECAUSE ONE CRASHED ONCE!!! I'm not a programmer, but I do look at the code where the Strings warnings are given, and the code posted usually does not involve the processes that supposedly invoke the "bug" as described by the "bug gurus". Giving String warnings that do not have to do with the issue being discussed is just a red flag for me that the people giving the warnings don't really understand the "bug" that they warning about.
Google forum search: Use Google Advanced Search and use Http://forum.arduino.cc/index in the "site or domain:" box.

Go Up