diff --git a/src/Configure.h b/src/Configure.h deleted file mode 100644 index 282bc1b..0000000 --- a/src/Configure.h +++ /dev/null @@ -1,20 +0,0 @@ - -#include -#include "Settings.h" - -/////////////////////////////// - -struct Configure { - - struct { - char ssid[MAX_SSID+1]; - char pwd[MAX_PWD+1]; - } wifiData; - - char setupCode[9]; - unsigned long timer; // time of trying to connect to WiFi - - void processRequest(WiFiClient &client, char *body, char *formData); - int getFormValue(char *formData, char *tag, char *value, int maxSize); - -}; diff --git a/src/HAP.cpp b/src/HAP.cpp index 737ffdc..62091ec 100644 --- a/src/HAP.cpp +++ b/src/HAP.cpp @@ -3,6 +3,7 @@ #include #include "HAP.h" +#include "Utils.h" ////////////////////////////////////// @@ -163,12 +164,6 @@ void HAPClient::processRequest(){ LOG2(body); LOG2("\n------------ END BODY! ------------\n"); - if(homeSpan.needsConfiguration){ // device not yet configured - content[cLen]='\0'; // add a trailing null on end of any contents, which should always be text-based - configure.processRequest(client, body, (char *)content); // process request - return; - } - if(!strncmp(body,"POST ",5)){ // this is a POST request if(cLen==0){ @@ -1571,5 +1566,4 @@ Accessory HAPClient::accessory; Controller HAPClient::controllers[MAX_CONTROLLERS]; SRP6A HAPClient::srp; int HAPClient::conNum; -Configure HAPClient::configure; diff --git a/src/HAP.h b/src/HAP.h index 6c4aa2d..ed4e755 100644 --- a/src/HAP.h +++ b/src/HAP.h @@ -7,7 +7,6 @@ #include "HKDF.h" #include "SRP.h" #include "HomeSpan.h" -#include "Configure.h" ///////////////////////////////////////////////// // NONCE Structure (HAP used last 64 of 96 bits) @@ -59,7 +58,6 @@ struct HAPClient { static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored static Controller controllers[MAX_CONTROLLERS]; // Paired Controller IDs and ED25519 long-term public keys - permanently stored static int conNum; // connection number - used to keep track of per-connection EV notifications - static Configure configure; // initial configuration of WiFi credentials and Setup Code // individual structures and data defined for each Hap Client connection @@ -121,30 +119,6 @@ struct HAPClient { static void eventNotify(SpanBuf *pObj, int nObj, int ignoreClient=-1); // transmits EVENT Notifications for nObj SpanBuf objects, pObj, with optional flag to ignore a specific client }; -///////////////////////////////////////////////// -// Creates a temporary buffer that is freed after -// going out of scope - -template -struct TempBuffer { - bufType *buf; - int nBytes; - - TempBuffer(size_t len){ - nBytes=len*sizeof(bufType); - buf=(bufType *)heap_caps_malloc(nBytes,MALLOC_CAP_8BIT); - } - - ~TempBuffer(){ - heap_caps_free(buf); - } - - int len(){ - return(nBytes); - } - -}; - ///////////////////////////////////////////////// // Extern Variables diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 18e21c3..55ee342 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -2,10 +2,10 @@ #include #include #include -#include #include "Utils.h" #include "HAP.h" +#include "Network.h" using namespace Utils; @@ -205,6 +205,8 @@ int Span::getFreeSlot(){ void Span::initWifi(){ + Network network; // initialization of WiFi credentials and Setup Code + struct { char ssid[MAX_SSID+1]; char pwd[MAX_PWD+1]; @@ -230,7 +232,7 @@ void Span::initWifi(){ } else { statusLED.start(500,0.3,2,1000); - configure(hostName); + network.configure(hostName); Serial.print("Please configure network...\n"); sprintf(wifiData.ssid,"MyNetwork"); @@ -334,61 +336,6 @@ void Span::initWifi(){ /////////////////////////////// -void Span::configure(char *apName){ - - Serial.print("WiFi Configuration required. Please connect to Access Point: "); - Serial.print(apName); - Serial.print("\n"); - - const byte DNS_PORT = 53; - WiFiServer apServer(80); - DNSServer dnsServer; - IPAddress apIP(192, 168, 4, 1); - - WiFi.mode(WIFI_AP); - WiFi.softAP(apName,"homespan",1,false,1); - dnsServer.start(DNS_PORT, "*", apIP); - apServer.begin(); - - needsConfiguration=true; - - while(needsConfiguration){ - - dnsServer.processNextRequest(); - - if(hap[0].client=apServer.available()){ // found a new HTTP client - LOG2("=======================================\n"); - LOG1("** Access Point Client Connected: ("); - LOG1(millis()/1000); - LOG1(" sec) "); - LOG1(hap[0].client.remoteIP()); - LOG1("\n"); - LOG2("\n"); - } - - if(hap[0].client && hap[0].client.available()){ // if connection exists and data is available - - HAPClient::conNum=0; // set connection number - hap[0].processRequest(); // process HAP request - - if(!hap[0].client){ // client disconnected by server - LOG1("** Disconnecting AP Client ("); - LOG1(millis()/1000); - LOG1(" sec)\n"); - } - - LOG2("\n"); - - } // process HAP Client - - } - - while(1); - -} - -/////////////////////////////// - void Span::processSerialCommand(char *c){ switch(c[0]){ diff --git a/src/HomeSpan.h b/src/HomeSpan.h index c14e742..0427094 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -54,8 +54,6 @@ struct Span{ unsigned long resetTime; // tracks time once reset button is pressed Blinker statusLED=Blinker(LED_BUILTIN); // indicates HomeSpan status - - boolean needsConfiguration=false; // tracks whether device needs to be configured with WiFi credentials and a Setup Code SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found vector Accessories; // vector of pointers to all Accessories @@ -73,7 +71,6 @@ struct Span{ int getFreeSlot(); // returns free HAPClient slot number. HAPClients slot keep track of each active HAPClient connection void initWifi(); // initialize and connect to WiFi network void processSerialCommand(char *c); // process command 'c' (typically from readSerial, though can be called with any 'c') - void configure(char *hostName); // configure homeSpan WiFi and Setup Code using temporary Captive Access Point 'hostName' int sprintfAttributes(char *cBuf); // prints Attributes JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL void prettyPrint(char *buf, int nsp=2); // print arbitrary JSON from buf to serial monitor, formatted with indentions of 'nsp' spaces diff --git a/src/Configure.cpp b/src/Network.cpp similarity index 55% rename from src/Configure.cpp rename to src/Network.cpp index afdff99..0fee554 100644 --- a/src/Configure.cpp +++ b/src/Network.cpp @@ -1,9 +1,108 @@ -#include "Configure.h" +#include + +#include "Network.h" +#include "Utils.h" /////////////////////////////// -void Configure::processRequest(WiFiClient &client, char *body, char *formData){ +void Network::configure(char *apName){ + + Serial.print("WiFi Configuration required. Please enter details here of connect to Access Point: "); + Serial.print(apName); + Serial.print("\n"); + + WiFiServer apServer(80); + + TempBuffer tempBuffer(MAX_HTTP+1); + uint8_t *httpBuf=tempBuffer.buf; + + const byte DNS_PORT = 53; + DNSServer dnsServer; + IPAddress apIP(192, 168, 4, 1); + + WiFi.mode(WIFI_AP); + WiFi.softAP(apName,"homespan",1,false,1); // only allow one connection at a time + dnsServer.start(DNS_PORT, "*", apIP); + apServer.begin(); + + boolean needsConfiguration=true; + + while(needsConfiguration){ + + dnsServer.processNextRequest(); + + if(client=apServer.available()){ // found a new HTTP client + LOG2("=======================================\n"); + LOG1("** Access Point Client Connected: ("); + LOG1(millis()/1000); + LOG1(" sec) "); + LOG1(client.remoteIP()); + LOG1("\n"); + LOG2("\n"); + } + + if(client && client.available()){ // if connection exists and data is available + + LOG2("<<<<<<<<< "); + LOG2(client.remoteIP()); + LOG2(" <<<<<<<<<\n"); + + int nBytes=client.read(httpBuf,MAX_HTTP+1); // read all available bytes up to maximum allowed+1 + + if(nBytes>MAX_HTTP){ // exceeded maximum number of bytes allowed + badRequestError(); + Serial.print("\n*** ERROR: Exceeded maximum HTTP message length\n\n"); + continue; + } + + httpBuf[nBytes]='\0'; // add null character to enable string functions + char *body=(char *)httpBuf; // char pointer to start of HTTP Body + char *p; // char pointer used for searches + + if(!(p=strstr((char *)httpBuf,"\r\n\r\n"))){ + badRequestError(); + Serial.print("\n*** ERROR: Malformed HTTP request (can't find blank line indicating end of BODY)\n\n"); + continue; + } + + *p='\0'; // null-terminate end of HTTP Body to faciliate additional string processing + uint8_t *content=(uint8_t *)p+4; // byte pointer to start of optional HTTP Content + int cLen=0; // length of optional HTTP Content + + if((p=strstr(body,"Content-Length: "))) // Content-Length is specified + cLen=atoi(p+16); + if(nBytes!=strlen(body)+4+cLen){ + badRequestError(); + Serial.print("\n*** ERROR: Malformed HTTP request (Content-Length plus Body Length does not equal total number of bytes read)\n\n"); + continue; + } + + LOG2(body); + LOG2("\n------------ END BODY! ------------\n"); + + content[cLen]='\0'; // add a trailing null on end of any contents, which should always be text-based + processRequest(body, (char *)content); // process request + + if(!client){ // client disconnected by server + LOG1("** Disconnecting AP Client ("); + LOG1(millis()/1000); + LOG1(" sec)\n"); + } + + LOG2("\n"); + + } // process HAP Client + + } + + while(1); + +} + +/////////////////////////////// + +void Network::processRequest(char *body, char *formData){ String responseHead="HTTP/1.1 200 OK\r\nContent-type: text/html\r\n"; @@ -94,7 +193,7 @@ void Configure::processRequest(WiFiClient &client, char *body, char *formData){ ////////////////////////////////////// -int Configure::getFormValue(char *formData, char *tag, char *value, int maxSize){ +int Network::getFormValue(char *formData, char *tag, char *value, int maxSize){ char *s=strstr(formData,tag); // find start of tag @@ -120,3 +219,21 @@ int Configure::getFormValue(char *formData, char *tag, char *value, int maxSize) } ////////////////////////////////////// + +int Network::badRequestError(){ + + char s[]="HTTP/1.1 400 Bad Request\r\n\r\n"; + LOG2("\n>>>>>>>>>> "); + LOG2(client.remoteIP()); + LOG2(" >>>>>>>>>>\n"); + LOG2(s); + client.print(s); + LOG2("------------ SENT! --------------\n"); + + delay(1); + client.stop(); + + return(-1); +} + +////////////////////////////////////// diff --git a/src/Network.h b/src/Network.h new file mode 100644 index 0000000..b2ac5be --- /dev/null +++ b/src/Network.h @@ -0,0 +1,26 @@ + +#include +#include "Settings.h" + +/////////////////////////////// + +struct Network { + + struct { + char ssid[MAX_SSID+1]; + char pwd[MAX_PWD+1]; + } wifiData; + + const int MAX_HTTP=4095; // max number of bytes in HTTP message + + char setupCode[9]; // 8-digit Setup Code with room for null terminator + + WiFiClient client; // client used for HTTP calls + unsigned long timer; // time of trying to connect to WiFi + + void configure(char *hostName); // configure homeSpan WiFi and Setup Code using temporary Captive Access Point 'hostName' + void processRequest(char *body, char *formData); // process the HTTP request + int getFormValue(char *formData, char *tag, char *value, int maxSize); // search for 'tag' in 'formData' and copy result into 'value' up to 'maxSize' characters; returns number of characters, else -1 if 'tag' not found + int badRequestError(); // return 400 error + +}; diff --git a/src/Utils.h b/src/Utils.h index e116bf0..a6cbf3b 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -7,3 +7,27 @@ char *readSerial(char *c, int max); // read serial port into 'c' until +struct TempBuffer { + bufType *buf; + int nBytes; + + TempBuffer(size_t len){ + nBytes=len*sizeof(bufType); + buf=(bufType *)heap_caps_malloc(nBytes,MALLOC_CAP_8BIT); + } + + ~TempBuffer(){ + heap_caps_free(buf); + } + + int len(){ + return(nBytes); + } + +};