diff --git a/src/SSLClient.h b/src/SSLClient.h index 1ae0b9f..44baf6d 100644 --- a/src/SSLClient.h +++ b/src/SSLClient.h @@ -24,11 +24,8 @@ * This library was created to provide SSL functionality to the {@link https://learn.adafruit.com/adafruit-wiz5500-wiznet-ethernet-featherwing/overview} * Adafruit Ethernet shield. Since this shield does not implement SSL functionality on * its own, we need to use an external library: in this case BearSSL {@link https://bearssl.org/}, - * which is also use in the Arduino ESP8266 core. SSLClient will serve to implement the - * BearSSL functionality inbetween EthernetCLient and the User, such that the user will - * simply need to start with: - * SSLCLient client(ethCLient); - * And then call the functions they normally would with EthernetClient using SSLCLient. + * which is also used in the Arduino ESP8266 core. SSLClient will serve to implement the + * BearSSL functionality inbetween EthernetClient and the User, such that the user. * * This file specifically controls the class templating used to allow SSLClient to interface * with all of the CLient-based classes. To see details on the implementations of the functions @@ -38,12 +35,17 @@ #include #include "Client.h" #include "SSLClientImpl.h" +#include "SSLSession.h" #ifndef SSLClient_H_ #define SSLClient_H_ /** - * \brief This class serves as a templating proxy class for the SSLClientImpl to do the real work. + * \brief The main SSLClient class + * + * TODO: fix this blurb + * + * This class serves as a templating proxy class for the SSLClientImpl to do the real work. * * A problem arose when writing this class: I wanted the user to be able to construct * this class in a single line of code (e.g. SSLClient(EthernetClient())), but I also @@ -59,16 +61,23 @@ template class SSLClient : public SSLClientImpl { -/** static type checks +/** + * static checks * I'm a java developer, so I want to ensure that my inheritance is safe. * These checks ensure that all the functions we use on class C are - * actually present on class C. It does this by first checking that the - * class inherits from Client, and then that it contains a status() function. + * actually present on class C. It does this by checking that the + * class inherits from Client. + * + * Additionally, I ran into a lot of memory issues with large sessions caches. + * Since each session contains at max 352 bytes of memory, they eat of the + * stack quite quickly and can cause overflows. As a result, I have added a + * warning here to discourage the use of more than 3 sessions at a time. Any + * amount past that will require special modification of this library, and + * assumes you know what you are doing. */ static_assert(std::is_base_of::value, "C must be a Client Class!"); static_assert(SessionCache > 0 && SessionCache < 255, "There can be no less than one and no more than 255 sessions in the cache!"); static_assert(SessionCache <= 3, "You need to decrease the size of m_iobuf in order to have more than 3 sessions at once, otherwise memory issues will occur."); -// static_assert(std::is_function::value, "C must have a status() function!"); public: /** @@ -77,9 +86,10 @@ public: * We copy the client because we aren't sure the Client object * is going to exists past the inital creation of the SSLClient. * - * @pre The client class must be able to access the internet, as SSLClient - * cannot manage this for you. Additionally it is recommended that the analog_pin - * be set to input. + * @pre You will need to generate an array of trust_anchors (root certificates) + * based off of the domains you want to make SSL connections to. Check out the + * Wiki on the pycert-bearssl tool for a simple way to do this. + * @pre The analog_pin should be set to input. * * @param trust_anchors Trust anchors used in the verification * of the SSL server certificate, generated using the `brssl` command @@ -88,13 +98,12 @@ public: * @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG * @param debug whether to enable or disable debug logging, must be constexpr */ - explicit SSLClient(const C& client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug = SSL_ERROR) + explicit SSLClient(const C& client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug = SSL_WARN) : SSLClientImpl(NULL, trust_anchors, trust_anchors_num, analog_pin, debug) , m_client(client) , m_sessions{SSLSession()} , m_index(0) { - // for (uint8_t i = 0; i < SessionCache; i++) m_sessions[i] = SSLSession(); // since we are copying the client in the ctor, we have to set // the client pointer after the class is constructed set_client(&m_client); @@ -106,23 +115,69 @@ public: /* * The special functions most clients have are below * Most of them smply pass through + */ + /** + * @brief Equivalent to SSLClient::connected() > 0 + * @returns true if connected, false if not */ virtual operator bool() { return connected() > 0; } + /** {@link SSLClient::bool()} */ virtual bool operator==(const bool value) { return bool() == value; } + /** {@link SSLClient::bool()} */ virtual bool operator!=(const bool value) { return bool() != value; } + /** @brief Returns whether or not two SSLClient objects have the same underlying client object */ virtual bool operator==(const C& rhs) { return m_client == rhs; } + /** @brief Returns whether or not two SSLClient objects do not have the same underlying client object */ virtual bool operator!=(const C& rhs) { return m_client != rhs; } - virtual uint16_t localPort() { return std::is_member_function_pointer::value ? m_client.localPort() : 0; } - virtual IPAddress remoteIP() { return std::is_member_function_pointer::value ? m_client.remoteIP() : INADDR_NONE; } - virtual uint16_t remotePort() { return std::is_member_function_pointer::value ? m_client.remotePort() : 0; } + /** @brief Returns the local port, if the Client class has a localPort() function. Else return 0. */ + virtual uint16_t localPort() { + if (std::is_member_function_pointer::value) return m_client.localPort(); + else { + m_warn("Client class has no localPort function, so localPort() will always return 0", __func__); + return 0; + } + } + /** @brief Returns the remote IP, if the Client class has a remoteIP() function. Else return INADDR_NONE. */ + virtual IPAddress remoteIP() { + if (std::is_member_function_pointer::value) return m_client.remoteIP(); + else { + m_warn("Client class has no remoteIP function, so remoteIP() will always return INADDR_NONE. This means that sessions caching will always be disabled.", __func__); + return INADDR_NONE; + } + } + /** @brief Returns the remote port, if the Client class has a remotePort() function. Else return 0. */ + virtual uint16_t remotePort() { + if (std::is_member_function_pointer::value) return m_client.remotePort(); + else { + m_warn("Client class has no remotePort function, so remotePort() will always return 0", __func__); + return 0; + } + } - //! get the client object + /** @brief returns a refernence to the client object stored in this class. Take care not to break it. */ C& getClient() { return m_client; } + /** + * @brief Get a sesssion reference corressponding to a host and IP, or a reference to a emptey session if none exist + * + * If no session corresponding to the host and ip exist, then this function will cycle through + * sessions in a rotating order. This allows the ssession cache to continuially store sessions, + * however it will also result in old sessions being cleared and returned. In general, it is a + * good idea to use a SessionCache size equal to the number of domains you plan on connecting to. + * + * @param host A hostname c string, or NULL if one is not availible + * @param ip An IP address + * @returns A reference to an SSLSession object + */ virtual SSLSession& getSession(const char* host, const IPAddress& addr); + /** + * @brief Clear the session corresponding to a host and IP + * + * @param host A hostname c string, or NULL if one is not availible + * @param ip An IP address + */ virtual void removeSession(const char* host, const IPAddress& addr); - private: // utility function to find a session index based off of a host and IP int m_getSessionIndex(const char* host, const IPAddress& addr) const; diff --git a/src/SSLClientImpl.cpp b/src/SSLClientImpl.cpp index 478af65..79f8fd8 100644 --- a/src/SSLClientImpl.cpp +++ b/src/SSLClientImpl.cpp @@ -20,6 +20,35 @@ #include "SSLClient.h" +// system reset definitions +static constexpr auto SYSRESETREQ = (1<<2); +static constexpr auto VECTKEY = (0x05fa0000UL); +static constexpr auto VECTKEY_MASK = (0x0000ffffUL); +/** Trigger a software reset. Only use if in unrecoverable state */ +[[ noreturn ]] static void RESET() { + (*(uint32_t*)0xe000ed0cUL)=((*(uint32_t*)0xe000ed0cUL)&VECTKEY_MASK)|VECTKEY|SYSRESETREQ; + while(1) { } +} + +#ifdef __arm__ +// should use uinstd.h to define sbrk but Due causes a conflict +extern "C" char* sbrk(int incr); +#else // __ARM__ +extern char *__brkval; +#endif // __arm__ + +/** Get the free memory availible in bytes (stack - heap) */ +static int freeMemory() { + char top; +#ifdef __arm__ + return &top - reinterpret_cast(sbrk(0)); +#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151) + return &top - __brkval; +#else // __arm__ + return __brkval ? &top - __brkval : &top - __malloc_heap_start; +#endif // __arm__ +} + /** see SSLClientImpl.h */ SSLClientImpl::SSLClientImpl(Client *client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug) @@ -473,6 +502,16 @@ unsigned SSLClientImpl::m_update_engine() { const auto avail = m_client->available(); if (avail >= len) { int mem = freeMemory(); + // check for a stack overflow + // if the stack overflows we basically have to crash, and + // hope the user is ok with that + // since all memory is garbage we can't trust the cpu to + // execute anything properly + if (mem > 32 * 1024) { + // software reset + RESET(); + } + // debug info m_info("Memory: ", func_name); m_info(mem, func_name); // free memory check @@ -485,17 +524,6 @@ unsigned SSLClientImpl::m_update_engine() { stop(); return 0; } - // check for a stack overflow - // if the stack overflows we basically have to crash, and - // hope the user is ok with that - // since all memory is garbage we can't trust the cpu to - // execute anything properly - if (mem > 32 * 1024) { - // software reset - REQUEST_EXTERNAL_RESET; - // can't print anything, so pray for reset - while (1) { } - } m_info("Read bytes from client: ", func_name); m_info(avail, func_name); m_info(len, func_name); diff --git a/src/SSLClientImpl.h b/src/SSLClientImpl.h index 82ea3fe..b38ebb3 100644 --- a/src/SSLClientImpl.h +++ b/src/SSLClientImpl.h @@ -26,68 +26,60 @@ #ifndef SSLClientImpl_H_ #define SSLClientImpl_H_ -// system reset definitions -#define SYSRESETREQ (1<<2) -#define VECTKEY (0x05fa0000UL) -#define VECTKEY_MASK (0x0000ffffUL) -#define AIRCR (*(uint32_t*)0xe000ed0cUL) // fixed arch-defined address -#define REQUEST_EXTERNAL_RESET (AIRCR=(AIRCR&VECTKEY_MASK)|VECTKEY|SYSRESETREQ) - -/** error enums - * Static constants defining the possible errors encountered - * Read from getWriteError(); +/** + * @brief Static constants defining the possible errors encountered. + * + * If SSLClient encounters an error, it will generally output + * logs into the serial moniter. If you need a way of programmatically + * checking the errors, you can do so with SSLCLient.getWriteError(), + * which will return one of these values. */ enum Error { SSL_OK = 0, + /** The underlying client failed to connect, probably not an issue with SSL */ SSL_CLIENT_CONNECT_FAIL, + /** BearSSL failed to complete the SSL handshake, check logs for bear ssl error output */ SSL_BR_CONNECT_FAIL, + /** The underlying client failed to write a payload, probably not an issue with SSL */ SSL_CLIENT_WRTIE_ERROR, + /** An internal error occurred with BearSSL, check logs for diagnosis. */ SSL_BR_WRITE_ERROR, + /** An internal error occured with SSLClient, and you probably need to submit an issue on Github. */ SSL_INTERNAL_ERROR, + /** SSLClient detected that there was not enough memory (>8000 bytes) to continue. */ SSL_OUT_OF_MEMORY }; -/** Debug level enum - * Static enum defining the debugging levels to print - * into the Serial monitor +/** + * @brief Level of verbosity used in logging for SSLClient. + * + * Use these values when initializing SSLCLient to set how many logs you + * would like to see in the Serial moniter. */ enum DebugLevel { + /** No logging output */ SSL_NONE = 0, + /** Only output errors that result in connection failure */ SSL_ERROR = 1, + /** Ouput errors and warnings (useful when just starting to develop) */ SSL_WARN = 2, + /** Output errors, warnings, and internal information (very verbose) */ SSL_INFO = 3, }; - -#ifdef __arm__ -// should use uinstd.h to define sbrk but Due causes a conflict -extern "C" char* sbrk(int incr); -#else // __ARM__ -extern char *__brkval; -#endif // __arm__ - -static int freeMemory() { - char top; -#ifdef __arm__ - return &top - reinterpret_cast(sbrk(0)); -#elif defined(CORE_TEENSY) || (ARDUINO > 103 && ARDUINO != 151) - return &top - __brkval; -#else // __arm__ - return __brkval ? &top - __brkval : &top - __malloc_heap_start; -#endif // __arm__ -} - - -/** TODO: Write what this is */ +/** + * On error, any function in this class will terminate the socket. + * TODO: Write what this is */ class SSLClientImpl : public Client { public: /** * @brief initializes SSL contexts for bearSSL * - * @pre The client class must be able to access the internet, as SSLClient - * cannot manage this for you. Additionally it is recommended that the analog_pin - * be set to input. + * @pre You will need to generate an array of trust_anchors (root certificates) + * based off of the domains you want to make SSL connections to. Check out the + * Wiki on the pycert-bearssl tool for a simple way to do this. + * @pre The analog_pin should be set to input. * * @post set_client must be called immediatly after to set the client class * pointer. @@ -104,13 +96,8 @@ public: */ explicit SSLClientImpl(Client* client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug); - /** Dtor is implicit since unique_ptr handles it fine */ - /** functions specific to the EthernetClient which I'll have to override */ - // uint8_t status(); - // uint8_t getSocketNumber() const; - - /** functions dealing with read/write that BearSSL will be injected into */ + /* functions dealing with read/write that BearSSL will be injected into */ /** * @brief Connect over SSL to a host specified by an ip address * @@ -122,46 +109,162 @@ public: * * This function initializes EthernetClient by calling EthernetClient::connect * with the parameters supplied, then once the socket is open initializes - * the appropriete bearssl contexts using the TLS_only_profile. Due to the - * design of the SSL standard, this function will probably take an extended - * period (1-2sec) to negotiate the handshake and finish the connection. + * the appropriete bearssl contexts. Due to the design of the SSL standard, + * this function will probably take an extended period (1-4sec) to negotiate + * the handshake and finish the connection. This function runs until the SSL + * handshake succeeds or fails, as found in most Arduino libraries, so be + * sure to design around this in your code. + * + * @pre The underlying client object (passed in through the ctor) in a non- + * error state, and must be able to access the server being connected to. + * @pre SSLCLient can only have one connection at a time, so the client + * object must not already have a socket open. + * @pre There must be sufficient memory availible on the device to verify + * the certificate (if the free memory drops below 8000 bytes during certain + * points in the connection, SSLCLient will fail). + * @pre There must be a trust anchor given to the ctor that corresponds to + * the certificate provided by the IP address being connected to. For more + * information check out the wiki on the pycert-bearssl tool. + * @pre The analog pin passed to the ctor must be set to input, and must + * be wired to something sort of random (floating is fine). * * @param ip The ip address to connect to * @param port the port to connect to * @returns 1 if success, 0 if failure (as found in EthernetClient) - * - * @error SSL_CLIENT_CONNECT_FAIL The client object could not connect to the host or port - * @error SSL_BR_CONNECT_FAIL BearSSL could not initialize the SSL connection. */ virtual int connect(IPAddress ip, uint16_t port); + /** - * @brief Connect over SSL using connect(ip, port), but use a DNS lookup to + * @brief Connect over SSL using connect(ip, port), using a DNS lookup to * get the IP Address first. * * This function initializes EthernetClient by calling EthernetClient::connect - * with the parameters supplied, then once the socket is open initializes - * the appropriete bearssl contexts using the TLS_only_profile. + * with the parameters supplied, then once the socket is open uses BearSSL to + * to complete a SSL handshake. This function runs until the SSL handshake + * succeeds or fails, as found in most Arduino libraries. + * + * SSL requires the client to generate some random bits (to be later combined + * with some random bits from the server), so SSLClient uses the least signinigant + * bits from the analog pin supplied in the ctor. The random bits are generated + * from 16 consecutive analogReads, and given to BearSSL before the handshake + * starts. * * Due to the design of the SSL standard, this function will probably take an - * extended period (1-2sec) to negotiate the handshake and finish the + * extended period (1-4sec) to negotiate the handshake and finish the * connection. Since the hostname is provided, however, BearSSL is able to keep * a session cache of the clients we have connected to. This should reduce - * connection time greatly. In order to use this feature, you must reuse the - * same SSLClient object to connect to the reused host. Doing this will allow - * BearSSL to automatically match the hostname to a cached session. + * connection time to about 100-200ms. In order to use this feature, the website + * you are connecting to must support it (most do by default), you must + * reuse the same SSLClient object, and you must reconnect to the same server. + * SSLClient automatcally stores an IP address and hostname in each session, + * ensuring that if you call connect("www.google.com") SSLClient will use a + * cached IP address instead of another DNS lookup. Because some websites have + * multiple servers on a single IP address (github.com is an example), however, + * you may find that even if you are connecting to the same host the connection + * does not resume. This is a flaw in the SSL session protocol, and has been + * resolved in future versions. On top of all that, SSL sessions can expire + * based on server criteria, which will result in a regular connection time. + * Because of all these factors, it is generally prudent to assume the + * connection will not be resumed, and go from there. + * + * @pre The underlying client object (passed in through the ctor) in a non- + * error state, and must be able to access the server being connected to. + * @pre SSLCLient can only have one connection at a time, so the client + * object must not already have a socket open. + * @pre There must be sufficient memory availible on the device to verify + * the certificate (if the free memory drops below 8000 bytes during certain + * points in the connection, SSLCLient will fail). + * @pre There must be a trust anchor given to the ctor that corresponds to + * the certificate provided by the IP address being connected to. For more + * information check out the wiki on the pycert-bearssl tool. + * @pre The analog pin passed to the ctor must be set to input, and must + * be wired to something sort of random (floating is fine). * * @param host The cstring host ("www.google.com") - * @param port the port to connect to - * @returns 1 of success, 0 if failure (as found in EthernetClient) - * - * @error SSL_CLIENT_CONNECT_FAIL The client object could not connect to the host or port - * @error SSL_BR_CONNECT_FAIL BearSSL could not initialize the SSL connection. + * @param port the port to connect to (443) + * @returns 1 of success, 0 if failure (as found in EthernetClient). */ virtual int connect(const char *host, uint16_t port); + + /** @see SSLClient::write(uint8_t*, size_t) */ virtual size_t write(uint8_t b) { return write(&b, 1); } + /** + * @brief Write some bytes to the SSL connection + * + * Assuming all preconditions are met, this function waits for BearSSL + * to be ready for data to be sent, then writes data to the BearSSL IO + * buffer, BUT does not initally send the data. Insead, it is + * then checked if the BearSSL IO buffer is full, and if so, this function + * waits until BearSSL has flushed the buffer (written it to the + * network client) and fills the buffer again. If the function finds + * that the BearSSL buffer is not full, it returns the number of + * bytes written. In other words, this function will only write data + * to the network if the BearSSL IO buffer is full. Instead, you must call + * SSLClient::availible or SSLClient::flush, which will detect that + * the buffer is ready for writing, and will write the data to the network. + * + * This was implemented as a buffered function because users of Arduino Client + * libraries will often write to the network as such: + * @code{.cpp} + * Client client; + * ... + * client.println("GET /asciilogo.txt HTTP/1.1"); + * client.println("Host: arduino.cc"); + * client.println("Connection: close"); + * while (!client.available()) { ... } + * ... + * @endcode + * This is fine with most network clients. With SSL, however, if we are encryting and + * writing to the network every write() call this will result in a lot of + * small encryption tasks. Encryption takes a lot of time and code, and in general + * the larger the batch we can do it in the better. For this reason, write() + * implicitly buffers until SSLClient::availible is called, or until the buffer is full. + * If you would like to trigger a network write manually without using the SSLClient::available, + * you can also call SSLClient::flush, which will write all data and return when finished. + * + * @pre The socket and SSL layer must be connected, meaning SSLClient::connected must be true. + * @pre BearSSL must not be waiting for the recipt of user data (if it is, there is + * probably an error with how the protocol in implemented in your code). + * + * @param buf the pointer to a buffer of bytes to copy + * @param size the number of bytes to copy from the buffer + * @returns The number of bytes copied to the buffer (size), or zero if the BearSSL engine + * fails to become ready for writing data. + */ virtual size_t write(const uint8_t *buf, size_t size); + + /** + * @brief Returns the number of bytes availible to read from the SSL Socket + * + * This function updates the state of the SSL engine (including writing any data, + * see SSLClient::write) and as a result should be called periodically when writing + * or expecting data. Additionally, since this function returns zero if there are + * no bytes and if SSLClient::connected is false (this same behavior is found + * in EthernetClient), it is prudent to ensure in your own code that the + * preconditions are met before checking this function to prevent an ambigious + * result. + * + * @pre SSLClient::connected must be true. + * + * @returns The number of bytes availible (can be zero), or zero if any of the pre + * conditions aren't satisfied. + */ virtual int available(); + + /** @see SSLClient::read(uint8_t*, size_t) */ virtual int read() { uint8_t read_val; return read(&read_val, 1) > 0 ? read_val : -1; } + /** + * @brief Read size bytes from the SSL socket buffer, copying them into *buf, and return the number of bytes read. + * + * This function checks if bytes are ready to be read by calling SSLClient::availible, + * and if there are some copies size number of bytes from the IO buffer into buf. + * + * It should be noted that a common problem I encountered with SSL connections is + * buffer overflow, caused by the server sending too much data at once. This problem + * only occurs... + * + * TODO: finish + */ virtual int read(uint8_t *buf, size_t size); virtual int peek(); virtual void flush(); diff --git a/src/SSLSession.h b/src/SSLSession.h index b881084..64442f9 100644 --- a/src/SSLSession.h +++ b/src/SSLSession.h @@ -50,13 +50,59 @@ class SSLSession : public br_ssl_session_parameters { public: + /** + * @brief SSLSession constructor + * + * Sets all parameters to zero, and invalidates the session + */ explicit SSLSession() : m_valid_session(false) , m_hostname() , m_ip(INADDR_NONE) {} + /** @brief use clear_parameters or set_parameters instead */ + SSLSession& operator=(const SSLSession&) = delete; + /** - * \pre must call br_ssl_engine_get_session_parameters(engine, toBearSSlSession()); + * @brief Get the hostname string associated with this session + * + * @returns A String object or "" if there is no hostname + * @pre must check isValidSession before getting this value, + * as if this session in invalid this value is not guarented + * to be reset to "". + */ + const String& get_hostname() const { return m_hostname; } + + /** + * @brief Get ::IPAddress associated with this session + * + * @returns A ::IPAddress object, #INADDR_NONE if there is no IP + * @pre must check isValidSession before getting this value, + * as if this session in invalid this value is not guarented + * to be reset to #INADDR_NONE. + */ + const IPAddress& get_ip() const { return m_ip; } + + bool is_valid_session() const { return m_valid_session; } + + /** + * @brief Set the ip address and hostname of the session. + * + * This function stores the ip Address object and hostname object into + * the session object. If hostname is not null or ip address is + * not blank, and the ::br_ssl_session_parameters values are non-zero + * it then validates the session. + * + * @pre You must call + * ::br_ssl_engine_get_session_parameters + * with this session before calling this function. This is because + * there is no way to completly validate the ::br_ssl_session_parameters + * and the session may end up in a corrupted state if this is not observed. + * + * @param ip The IP address of the host associated with the session + * @param hostname The string hostname ("www.google.com") associated with the session. + * Take care that this value is corrent, SSLSession performs no validation + * of the hostname. */ void set_parameters(const IPAddress& ip, const char* hostname = NULL) { // copy the hostname @@ -68,8 +114,16 @@ public: // check if both values are valid, and if so set valid to true if (m_ip != INADDR_NONE && session_id_len > 0 && (hostname == NULL || m_hostname)) m_valid_session = true; + // else clear + else clear_parameters(); } + /** + * @brief delete the parameters and invalidate the session + * Roughly equivalent to this_session = SSLSession(), however + * this function preserves the String object, allowing it + * to better handle the dynamic memory needed. + */ void clear_parameters() { // clear the hostname , ip, and valid session flags m_hostname = ""; @@ -77,21 +131,9 @@ public: m_valid_session = false; } - SSLSession& operator=(const SSLSession&) = delete; - + /** @brief returns a pointer to the ::br_ssl_session_parameters component of this class */ br_ssl_session_parameters* to_br_session() { return (br_ssl_session_parameters *)this; } - /** - * \pre must check isValidSession - */ - const String& get_hostname() const { return m_hostname; } - - /** - * \pre must check isValidSession - */ - const IPAddress& get_ip() const { return m_ip; } - - bool is_valid_session() const { return m_valid_session; } private: bool m_valid_session; // aparently a hostname has a max length of 256 chars. Go figure.