Offline
Full Member
Karma: 0
Posts: 190
|
 |
« on: February 04, 2013, 06:22:49 pm » |
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?
|
|
|
|
|
Logged
|
|
|
|
|
Miramar Beach, Florida
Offline
Faraday Member
Karma: 60
Posts: 3547
|
 |
« Reply #1 on: February 04, 2013, 07:05:52 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #2 on: February 05, 2013, 10:58:13 am » |
Why not using HTTP authentication and checking the client's password at every request?
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Full Member
Karma: 0
Posts: 190
|
 |
« Reply #3 on: February 05, 2013, 04:13:48 pm » |
Yea I'm actually one step ahead of you on that. But how do you log the client out?
|
|
|
|
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #4 on: February 06, 2013, 05:55:53 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Full Member
Karma: 0
Posts: 190
|
 |
« Reply #5 on: February 06, 2013, 09:16:54 am » |
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
|
|
|
|
« Last Edit: February 06, 2013, 09:33:25 am by ematson5897 »
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #6 on: February 06, 2013, 10:28:11 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Full Member
Karma: 0
Posts: 190
|
 |
« Reply #7 on: February 06, 2013, 03:53:11 pm » |
Ok. Heres my code so far: #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
|
|
|
|
« Last Edit: February 06, 2013, 04:16:56 pm by ematson5897 »
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #8 on: February 07, 2013, 06:16:30 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Full Member
Karma: 0
Posts: 190
|
 |
« Reply #9 on: February 07, 2013, 03:01:41 pm » |
Ok. I guess I need to whip up a function to replicate indexOf in a char array EDIT: Got it. Now for implementation int findStr(char * strA, char * strB) { int location = strstr(strA , strB)-strA+1; if(location<0) { return -1; } return location; }
|
|
|
|
« Last Edit: February 07, 2013, 04:21:38 pm by ematson5897 »
|
Logged
|
|
|
|
|
0
Online
Tesla Member
Karma: 58
Posts: 6781
Arduino rocks
|
 |
« Reply #10 on: February 07, 2013, 11:38:56 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #11 on: February 08, 2013, 10:20:19 am » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Online
Tesla Member
Karma: 58
Posts: 6781
Arduino rocks
|
 |
« Reply #12 on: February 08, 2013, 11:54:19 am » |
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. String buffer = "";
|
|
|
|
« Last Edit: February 08, 2013, 11:56:33 am by zoomkat »
|
Logged
|
|
|
|
|
Switzerland
Offline
Faraday Member
Karma: 71
Posts: 3486
|
 |
« Reply #13 on: February 08, 2013, 12:42:43 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
0
Online
Tesla Member
Karma: 58
Posts: 6781
Arduino rocks
|
 |
« Reply #14 on: February 08, 2013, 02:32:55 pm » |
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.
|
|
|
|
|
Logged
|
|
|
|
|
|