diff --git a/cores/esp8266/FS.cpp b/cores/esp8266/FS.cpp index d7217df01..e3158713d 100644 --- a/cores/esp8266/FS.cpp +++ b/cores/esp8266/FS.cpp @@ -193,6 +193,17 @@ File FS::open(const char* path, const char* mode) { return File(_impl->open(path, om, am)); } +bool FS::exists(const char* path) { + if (!_impl) { + return false; + } + return _impl->exists(path); +} + +bool FS::exists(const String& path) { + return exists(path.c_str()); +} + Dir FS::openDir(const char* path) { if (!_impl) { return Dir(); diff --git a/cores/esp8266/FS.h b/cores/esp8266/FS.h index 1301a631d..d7a170d1d 100644 --- a/cores/esp8266/FS.h +++ b/cores/esp8266/FS.h @@ -97,6 +97,9 @@ public: File open(const char* path, const char* mode); File open(const String& path, const char* mode); + bool exists(const char* path); + bool exists(const String& path); + Dir openDir(const char* path); Dir openDir(const String& path); diff --git a/cores/esp8266/FSImpl.h b/cores/esp8266/FSImpl.h index b6bed005e..b95bafcfc 100644 --- a/cores/esp8266/FSImpl.h +++ b/cores/esp8266/FSImpl.h @@ -65,6 +65,7 @@ public: virtual bool begin() = 0; virtual bool format() = 0; virtual FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) = 0; + virtual bool exists(const char* path) = 0; virtual DirImplPtr openDir(const char* path) = 0; virtual bool rename(const char* pathFrom, const char* pathTo) = 0; virtual bool remove(const char* path) = 0; diff --git a/cores/esp8266/cbuf.h b/cores/esp8266/cbuf.h index 728547fad..fee98a94b 100644 --- a/cores/esp8266/cbuf.h +++ b/cores/esp8266/cbuf.h @@ -95,7 +95,7 @@ class cbuf { size_t bytes_available = room(); size_t size_to_write = (size < bytes_available) ? size : bytes_available; size_t size_written = size_to_write; - if(_end > _begin && size_to_write > (size_t)(_bufend - _end)) { + if(_end >= _begin && size_to_write > (size_t)(_bufend - _end)) { size_t top_size = _bufend - _end; memcpy(_end, src, top_size); _end = _buf; diff --git a/cores/esp8266/spiffs_api.cpp b/cores/esp8266/spiffs_api.cpp index db8311876..4de08c405 100644 --- a/cores/esp8266/spiffs_api.cpp +++ b/cores/esp8266/spiffs_api.cpp @@ -59,7 +59,7 @@ public: } FileImplPtr open(const char* path, OpenMode openMode, AccessMode accessMode) override; - + bool exists(const char* path) override; DirImplPtr openDir(const char* path) override; bool rename(const char* pathFrom, const char* pathTo) override { @@ -404,6 +404,14 @@ FileImplPtr SPIFFSImpl::open(const char* path, OpenMode openMode, AccessMode acc return std::make_shared(this, fd); } +bool SPIFFSImpl::exists(const char* path) { + char tmpName[SPIFFS_OBJ_NAME_LEN]; + strlcpy(tmpName, path, sizeof(tmpName)); + spiffs_stat stat; + int rc = SPIFFS_stat(&_fs, tmpName, &stat); + return rc == SPIFFS_OK; +} + DirImplPtr SPIFFSImpl::openDir(const char* path) { spiffs_DIR dir; char tmpName[SPIFFS_OBJ_NAME_LEN]; diff --git a/doc/reference.md b/doc/reference.md index 5dbd0b9c1..ec0b9038d 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -231,6 +231,14 @@ if (!f) { } ``` +#### exists + +```c++ +SPIFFS.exists(path) +``` + +Returns *true* if a file with given path exists, *false* otherwise. + #### openDir ```c++ diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 3af856184..75474ae70 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -40,8 +40,7 @@ ESP8266WebServer::ESP8266WebServer(int port) { } -ESP8266WebServer::~ESP8266WebServer() -{ +ESP8266WebServer::~ESP8266WebServer() { if (!_firstHandler) return; RequestHandler* handler = _firstHandler; @@ -56,14 +55,11 @@ void ESP8266WebServer::begin() { _server.begin(); } - -void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler) -{ +void ESP8266WebServer::on(const char* uri, ESP8266WebServer::THandlerFunction handler) { on(uri, HTTP_ANY, handler); } -void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) -{ +void ESP8266WebServer::on(const char* uri, HTTPMethod method, ESP8266WebServer::THandlerFunction fn) { _addRequestHandler(new FunctionRequestHandler(fn, uri, method)); } @@ -79,11 +75,10 @@ void ESP8266WebServer::_addRequestHandler(RequestHandler* handler) { } void ESP8266WebServer::serveStatic(const char* uri, FS& fs, const char* path) { - _addRequestHandler(new StaticRequestHandler(fs, uri)); + _addRequestHandler(new StaticRequestHandler(fs, path, uri)); } -void ESP8266WebServer::handleClient() -{ +void ESP8266WebServer::handleClient() { WiFiClient client = _server.available(); if (!client) { return; diff --git a/libraries/ESP8266WebServer/src/detail/RequestHandler.h b/libraries/ESP8266WebServer/src/detail/RequestHandler.h index 702e24e0b..8cb4e61e1 100644 --- a/libraries/ESP8266WebServer/src/detail/RequestHandler.h +++ b/libraries/ESP8266WebServer/src/detail/RequestHandler.h @@ -4,8 +4,8 @@ class RequestHandler { public: RequestHandler(const char* uri, HTTPMethod method) - : uri(uri) - , method(method) + : _uri(uri) + , _method(method) , next(NULL) { } @@ -15,8 +15,8 @@ public: RequestHandler* next; protected: - String uri; - HTTPMethod method; + String _uri; + HTTPMethod _method; }; @@ -25,47 +25,59 @@ class FunctionRequestHandler : public RequestHandler { public: FunctionRequestHandler(ESP8266WebServer::THandlerFunction fn, const char* uri, HTTPMethod method) - : fn(fn) + : _fn(fn) , base(uri, method) { } bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { - if (method != HTTP_ANY && method != requestMethod) + if (_method != HTTP_ANY && _method != requestMethod) return false; - if (requestUri != uri) + if (requestUri != _uri) return false; - fn(); + _fn(); return true; } protected: - ESP8266WebServer::THandlerFunction fn; + ESP8266WebServer::THandlerFunction _fn; }; class StaticRequestHandler : public RequestHandler { typedef RequestHandler base; public: - StaticRequestHandler(FS& fs, const char* uri) - : fs(fs) + StaticRequestHandler(FS& fs, const char* path, const char* uri) + : _fs(fs) , base(uri, HTTP_GET) + , _path(path) { + _isFile = fs.exists(path); + DEBUGV("StaticRequestHandler: path=%s uri=%s isFile=%d\r\n", path, uri, _isFile); + _baseUriLength = _uri.length(); } bool handle(ESP8266WebServer& server, HTTPMethod requestMethod, String requestUri) override { - if (requestMethod != method) + if (requestMethod != _method) return false; - DEBUGV("StaticRequestHandler::handle: %s\r\n", requestUri.c_str()); - if (!requestUri.startsWith(uri)) + DEBUGV("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str()); + if (!requestUri.startsWith(_uri)) return false; - auto prefixLength = uri.length() - 1; - String path = requestUri.substring(prefixLength); - DEBUGV("StaticRequestHandler::handle: %d %s\r\n", prefixLength, path.c_str()); - File f = fs.open(path, "r"); + String path(_path); + if (!_isFile) { + // Base URI doesn't point to a file. Append whatever follows this + // URI in request to get the file path. + path += requestUri.substring(_baseUriLength); + } + else if (requestUri != _uri) { + // Base URI points to a file but request doesn't match this URI exactly + return false; + } + DEBUGV("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile); + File f = _fs.open(path, "r"); if (!f) return false; @@ -90,7 +102,10 @@ public: } protected: - FS fs; + FS _fs; + String _path; + bool _isFile; + size_t _baseUriLength; }; #endif //REQUESTHANDLER_H diff --git a/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino new file mode 100644 index 000000000..975fe13ef --- /dev/null +++ b/libraries/ESP8266WiFi/examples/HTTPSRequest/HTTPSRequest.ino @@ -0,0 +1,88 @@ +/* + * HTTP over TLS (HTTPS) example sketch + * + * This example demonstrates how to use + * WiFiClientSecure class to access HTTPS API. + * We fetch and display the status of + * esp8266/Arduino project continous integration + * build. + * + * Created by Ivan Grokhotkov, 2015. + * This example is in public domain. + */ + +#include +#include + +const char* ssid = "........"; +const char* password = "........"; + +const char* host = "api.github.com"; +const int httpsPort = 443; + +// Use web browser to view and copy +// SHA1 fingerprint of the certificate +const char* fingerprint = "CF 05 98 89 CA FF 8E D8 5E 5C E0 C2 E4 F7 E6 C3 C7 50 DD 5C"; + +void setup() { + Serial.begin(115200); + Serial.println(); + Serial.print("connecting to "); + Serial.println(ssid); + WiFi.begin(ssid, password); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.println(""); + Serial.println("WiFi connected"); + Serial.println("IP address: "); + Serial.println(WiFi.localIP()); + + // Use WiFiClientSecure class to create TLS connection + WiFiClientSecure client; + Serial.print("connecting to "); + Serial.println(host); + if (!client.connect(host, httpsPort)) { + Serial.println("connection failed"); + return; + } + + if (client.verify(fingerprint, host)) { + Serial.println("certificate matches"); + } else { + Serial.println("certificate doesn't match"); + } + + String url = "/repos/esp8266/Arduino/commits/esp8266/status"; + Serial.print("requesting URL: "); + Serial.println(url); + + client.print(String("GET ") + url + " HTTP/1.1\r\n" + + "Host: " + host + "\r\n" + + "User-Agent: BuildFailureDetectorESP8266\r\n" + + "Connection: close\r\n\r\n"); + + Serial.println("request sent"); + while (client.connected()) { + String line = client.readStringUntil('\n'); + if (line == "\r") { + Serial.println("headers received"); + break; + } + } + String line = client.readStringUntil('\n'); + if (line.startsWith("{\"state\":\"success\"")) { + Serial.println("esp8266/Arduino CI successfull!"); + } else { + Serial.println("esp8266/Arduino CI has failed"); + } + Serial.println("reply was:"); + Serial.println("=========="); + Serial.println(line); + Serial.println("=========="); + Serial.println("closing connection"); +} + +void loop() { +} diff --git a/libraries/ESP8266WiFi/keywords.txt b/libraries/ESP8266WiFi/keywords.txt index e693ea4da..131c6f64b 100644 --- a/libraries/ESP8266WiFi/keywords.txt +++ b/libraries/ESP8266WiFi/keywords.txt @@ -16,6 +16,7 @@ WiFi KEYWORD1 WiFiClient KEYWORD1 WiFiServer KEYWORD1 WiFiUDP KEYWORD1 +WiFiClientSecure KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) @@ -64,4 +65,3 @@ scanNetworks KEYWORD2 WIFI_AP LITERAL1 WIFI_STA LITERAL1 WIFI_AP_STA LITERAL1 - diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp index afa216dff..d5dd9c638 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.cpp @@ -29,6 +29,7 @@ extern "C" } #include #include "debug.h" +#include "cbuf.h" #include "ESP8266WiFi.h" #include "WiFiClientSecure.h" #include "WiFiClient.h" @@ -41,15 +42,23 @@ extern "C" #include "include/ClientContext.h" #include "c_types.h" +//#define DEBUG_SSL + +#ifdef DEBUG_SSL +#define SSL_DEBUG_OPTS SSL_DISPLAY_STATES +#else +#define SSL_DEBUG_OPTS 0 +#endif class SSLContext { public: SSLContext() { if (_ssl_ctx_refcnt == 0) { - _ssl_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DISPLAY_STATES, 0); + _ssl_ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER | SSL_DEBUG_OPTS, 0); } ++_ssl_ctx_refcnt; + _rxbuf = new cbuf(1536); } ~SSLContext() { @@ -62,6 +71,8 @@ public: if (_ssl_ctx_refcnt == 0) { ssl_ctx_free(_ssl_ctx); } + + delete _rxbuf; } void ref() { @@ -78,27 +89,71 @@ public: _ssl = ssl_client_new(_ssl_ctx, reinterpret_cast(ctx), nullptr, 0); } + int read(uint8_t* dst, size_t size) { + if (size > _rxbuf->getSize()) { + _readAll(); + } + return _rxbuf->read(reinterpret_cast(dst), size); + } + + int read() { + optimistic_yield(100); + if (!_rxbuf->getSize()) { + _readAll(); + } + return _rxbuf->read(); + } + + int peek() { + if (!_rxbuf->getSize()) { + _readAll(); + } + return _rxbuf->peek(); + } + + int available() { + optimistic_yield(100); + return _rxbuf->getSize(); + } + operator SSL*() { return _ssl; } protected: + int _readAll() { + uint8_t* data; + int rc = ssl_read(_ssl, &data); + if (rc <= 0) + return 0; + + if (rc > _rxbuf->room()) { + DEBUGV("WiFiClientSecure rx overflow"); + rc = _rxbuf->room(); + } + int result = 0; + size_t sizeBefore = _rxbuf->getSize(); + if (rc) + result = _rxbuf->write(reinterpret_cast(data), rc); + DEBUGV("*** rb: %d + %d = %d\r\n", sizeBefore, rc, _rxbuf->getSize()); + return result; + } + static SSL_CTX* _ssl_ctx; static int _ssl_ctx_refcnt; SSL* _ssl = nullptr; int _refcnt = 0; + cbuf* _rxbuf; }; SSL_CTX* SSLContext::_ssl_ctx = nullptr; int SSLContext::_ssl_ctx_refcnt = 0; -WiFiClientSecure::WiFiClientSecure() -{ +WiFiClientSecure::WiFiClientSecure() { } -WiFiClientSecure::~WiFiClientSecure() -{ +WiFiClientSecure::~WiFiClientSecure() { if (_ssl) { _ssl->unref(); } @@ -164,14 +219,19 @@ size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) { } int WiFiClientSecure::read(uint8_t *buf, size_t size) { + return _ssl->read(buf, size); +} - uint8_t* data; - int rc = ssl_read(*_ssl, &data); - if (rc <= 0) - return 0; +int WiFiClientSecure::read() { + return _ssl->read(); +} - memcpy(buf, data, rc); - return rc; +int WiFiClientSecure::peek() { + return _ssl->peek(); +} + +int WiFiClientSecure::available() { + return _ssl->available(); } void WiFiClientSecure::stop() { @@ -182,6 +242,50 @@ void WiFiClientSecure::stop() { return WiFiClient::stop(); } +static bool parseHexNibble(char pb, uint8_t* res) { + if (pb >= '0' && pb <= '9') { + *res = (uint8_t) (pb - '0'); return true; + } + else if (pb >= 'a' && pb <= 'f') { + *res = (uint8_t) (pb - 'a' + 10); return true; + } + else if (pb >= 'A' && pb <= 'F') { + *res = (uint8_t) (pb - 'A' + 10); return true; + } + return false; +} + +bool WiFiClientSecure::verify(const char* fp, const char* url) { + uint8_t sha1[20]; + int len = strlen(fp); + int pos = 0; + for (int i = 0; i < sizeof(sha1); ++i) { + while (pos < len && fp[pos] == ' ') { + ++pos; + } + DEBUGV("pos:%d ", pos); + if (pos > len - 2) { + DEBUGV("fingerprint too short\r\n"); + return false; + } + uint8_t high, low; + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) { + DEBUGV("invalid hex sequence: %c%c\r\n", fp[pos], fp[pos+1]); + return false; + } + pos += 2; + sha1[i] = low | (high << 4); + } + if (ssl_match_fingerprint(*_ssl, sha1) != 0) { + DEBUGV("fingerprint doesn't match\r\n"); + return false; + } + + //TODO: check URL against certificate + + return true; +} + extern "C" int ax_port_read(int fd, uint8_t* buffer, size_t count) { ClientContext* _client = reinterpret_cast(fd); if (_client->state() != ESTABLISHED && !_client->getSize()) { @@ -217,13 +321,13 @@ extern "C" int ax_get_file(const char *filename, uint8_t **buf) { return 0; } + #ifdef DEBUG_TLS_MEM #define DEBUG_TLS_MEM_PRINT(...) DEBUGV(__VA_ARGS__) #else #define DEBUG_TLS_MEM_PRINT(...) #endif - extern "C" void* ax_port_malloc(size_t size, const char* file, int line) { void* result = malloc(size); @@ -254,7 +358,6 @@ extern "C" void* ax_port_realloc(void* ptr, size_t size, const char* file, int l return result; } - extern "C" void ax_port_free(void* ptr) { free(ptr); uint32_t *p = (uint32_t*) ptr; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecure.h b/libraries/ESP8266WiFi/src/WiFiClientSecure.h index f45d8f843..3b3d2907c 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecure.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecure.h @@ -38,8 +38,13 @@ public: int connect(IPAddress ip, uint16_t port) override; int connect(const char* name, uint16_t port) override; + bool verify(const char* fingerprint, const char* url); + size_t write(const uint8_t *buf, size_t size) override; int read(uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; void stop() override; protected: diff --git a/libraries/ESP8266WiFi/src/include/ssl.h b/libraries/ESP8266WiFi/src/include/ssl.h index ef93886df..60e4d7e62 100644 --- a/libraries/ESP8266WiFi/src/include/ssl.h +++ b/libraries/ESP8266WiFi/src/include/ssl.h @@ -375,6 +375,15 @@ EXP_FUNC void STDCALL ssl_display_error(int error_code); */ EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl); +/** + * @brief Check if certificate fingerprint (SHA1) matches the one given. + * + * @param ssl [in] An SSL object reference. + * @param fp [in] SHA1 fingerprint to match against + * @return SSL_OK if the certificate is verified. + */ +EXP_FUNC int STDCALL ssl_match_fingerprint(const SSL *ssl, const uint8_t* fp); + /** * @brief Retrieve an X.509 distinguished name component. * diff --git a/tools/sdk/lib/libaxtls.a b/tools/sdk/lib/libaxtls.a index 806bdbbf4..7da3c1c1c 100644 Binary files a/tools/sdk/lib/libaxtls.a and b/tools/sdk/lib/libaxtls.a differ