diff --git a/examples/websocket_mbedtls/Makefile b/examples/websocket_mbedtls/Makefile new file mode 100644 index 0000000..3570a79 --- /dev/null +++ b/examples/websocket_mbedtls/Makefile @@ -0,0 +1,4 @@ +PROGRAM=websocket_mbedtls +COMPONENTS = FreeRTOS lwip core extras/mbedtls + +include ../../common.mk diff --git a/examples/websocket_mbedtls/adler32.c b/examples/websocket_mbedtls/adler32.c new file mode 100644 index 0000000..f99b2d7 --- /dev/null +++ b/examples/websocket_mbedtls/adler32.c @@ -0,0 +1,78 @@ +/* + * Adler-32 checksum + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +/* + * Adler-32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + +#include "tinf.h" + +#define A32_BASE 65521 +#define A32_NMAX 5552 + +unsigned int tinf_adler32(const void *data, unsigned int length) +{ + const unsigned char *buf = (const unsigned char *)data; + + unsigned int s1 = 1; + unsigned int s2 = 0; + + while (length > 0) + { + int k = length < A32_NMAX ? length : A32_NMAX; + int i; + + for (i = k / 16; i; --i, buf += 16) + { + s1 += buf[0]; s2 += s1; s1 += buf[1]; s2 += s1; + s1 += buf[2]; s2 += s1; s1 += buf[3]; s2 += s1; + s1 += buf[4]; s2 += s1; s1 += buf[5]; s2 += s1; + s1 += buf[6]; s2 += s1; s1 += buf[7]; s2 += s1; + + s1 += buf[8]; s2 += s1; s1 += buf[9]; s2 += s1; + s1 += buf[10]; s2 += s1; s1 += buf[11]; s2 += s1; + s1 += buf[12]; s2 += s1; s1 += buf[13]; s2 += s1; + s1 += buf[14]; s2 += s1; s1 += buf[15]; s2 += s1; + } + + for (i = k % 16; i; --i) { s1 += *buf++; s2 += s1; } + + s1 %= A32_BASE; + s2 %= A32_BASE; + + length -= k; + } + + return (s2 << 16) | s1; +} diff --git a/examples/websocket_mbedtls/cert.c b/examples/websocket_mbedtls/cert.c new file mode 100644 index 0000000..7acc438 --- /dev/null +++ b/examples/websocket_mbedtls/cert.c @@ -0,0 +1,44 @@ +/* This is the CA certificate for the CA trust chain of + www.howsmyssl.com in PEM format, as dumped via: + + openssl s_client -showcerts -connect www.howsmyssl.com:443 +#include +#include + +/* + 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 + i:/O=Digital Signature Trust Co./CN=DST Root CA X3 + */ +const char *server_root_cert = "-----BEGIN CERTIFICATE-----\r\n" +"MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\r\n" +"MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\r\n" +"DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\r\n" +"SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\r\n" +"GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\r\n" +"AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\r\n" +"q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\r\n" +"SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\r\n" +"Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\r\n" +"a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\r\n" +"/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\r\n" +"AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\r\n" +"CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\r\n" +"bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\r\n" +"c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\r\n" +"VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\r\n" +"ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\r\n" +"MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\r\n" +"Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\r\n" +"AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\r\n" +"uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\r\n" +"wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\r\n" +"X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\r\n" +"PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\r\n" +"KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\r\n" +"-----END CERTIFICATE-----\r\n"; + + diff --git a/examples/websocket_mbedtls/conn.c b/examples/websocket_mbedtls/conn.c new file mode 100644 index 0000000..6daeff4 --- /dev/null +++ b/examples/websocket_mbedtls/conn.c @@ -0,0 +1,229 @@ +#include "espressif/esp_common.h" +#include "esp/uart.h" + +#include + +#include "FreeRTOS.h" +#include "task.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" +#include "lwip/netdb.h" +#include "lwip/dns.h" +#include "lwip/api.h" + +#include "ssid_config.h" + +/* mbedtls/config.h MUST appear before all other mbedtls headers, or + you'll get the default config. + + (Although mostly that isn't a big problem, you just might get + errors at link time if functions don't exist.) */ +#include "mbedtls/config.h" + +#include "mbedtls/net.h" +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/error.h" +#include "mbedtls/certs.h" + +#include +#include + +#include "conn.h" + +/* SSL file descriptors */ +#define SSL_HANDLES_SIZE 4 +mbedtls_ssl_context* sslHandles[SSL_HANDLES_SIZE] = {NULL, NULL, NULL, NULL}; +unsigned char ctxC = 0; + +mbedtls_ssl_config conf; +mbedtls_x509_crt cacert; +mbedtls_ctr_drbg_context ctr_drbg; +mbedtls_entropy_context entropy; + +/* Connect to a hostname and port using TLS 1.2 and +return a index for one SSL connection on the pool or -1 if error +*/ +int ConnConnect(char *hostname, int port) { + int ret = 0; + mbedtls_ssl_context *sslFD; + mbedtls_net_context *socketFD; + int sslHandle = 0; + char s_port[10]; + + // configure mbedtls + if (!ctxC) { + mbedtls_ssl_config_init( &conf ); + mbedtls_x509_crt_init( &cacert ); + mbedtls_ctr_drbg_init( &ctr_drbg ); + mbedtls_entropy_init( &entropy ); + + if( ( ret = mbedtls_ctr_drbg_seed( &ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *) "ssl_client1", + strlen( "ssl_client1" ) ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret ); + return -1; + } + + ret = mbedtls_x509_crt_parse( &cacert, (const unsigned char *) mbedtls_test_cas_pem, + mbedtls_test_cas_pem_len ); + if( ret < 0 ) + { + printf( " failed\n ! mbedtls_x509_crt_parse returned -0x%x\n\n", -ret ); + return -1; + } + + + if( ( ret = mbedtls_ssl_config_defaults( &conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_config_defaults returned %d\n\n", ret ); + return -1; + } + + mbedtls_ssl_conf_authmode( &conf, MBEDTLS_SSL_VERIFY_OPTIONAL ); + mbedtls_ssl_conf_ca_chain( &conf, &cacert, NULL ); + mbedtls_ssl_conf_rng( &conf, mbedtls_ctr_drbg_random, &ctr_drbg ); + + ctxC = 1; + } + + // find a free slot at sslHandles + while(sslHandle < SSL_HANDLES_SIZE) { + if (sslHandles[sslHandle] == NULL) { + sslFD = malloc(sizeof(mbedtls_ssl_context)); + socketFD = malloc(sizeof(mbedtls_net_context)); + mbedtls_ssl_init( sslFD ); + mbedtls_net_init( socketFD ); + sslHandles[sslHandle] = sslFD; + break; + } + sslHandle++; + } + + // no free slot at sslHandles + if (sslHandle == SSL_HANDLES_SIZE) { + return -1; + } + + // connect mbedtls socket + memset(s_port, 0, sizeof(s_port)); + sprintf(s_port, "%d", port); + ret = mbedtls_net_connect(socketFD, hostname, s_port, MBEDTLS_NET_PROTO_TCP); + if (ret != 0) { + ConnClose(sslHandle); + return -1; + } + + if(( ret = mbedtls_ssl_setup( sslFD, &conf ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_setup returned %d\n\n", ret ); + ConnClose(sslHandle); + return -1; + } + + if( ( ret = mbedtls_ssl_set_hostname( sslFD, "mbed TLS Server 1" ) ) != 0 ) + { + printf( " failed\n ! mbedtls_ssl_set_hostname returned %d\n\n", ret ); + ConnClose(sslHandle); + return -1; + } + + mbedtls_ssl_set_bio( sslFD, socketFD, mbedtls_net_send, mbedtls_net_recv, NULL ); + + while( ( ret = mbedtls_ssl_handshake( sslFD ) ) != 0 ) + { + if( ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE ) + { + printf( " failed\n ! mbedtls_ssl_handshake returned -0x%x\n\n", -ret ); + ConnClose(sslHandle); + return -1; + } + } + + return sslHandle; +} + +int ConnReadBytesAvailable(int sslHandle) { + mbedtls_ssl_context* sslFD = NULL; + mbedtls_net_context* socketFD = NULL; + int count = 0; + + if (sslHandle >= 0) sslFD = sslHandles[sslHandle]; + else return -1; + + if (sslFD) { + socketFD = (mbedtls_net_context *) sslFD->p_bio; + lwip_ioctl(socketFD->fd, FIONREAD, &count); + return count; + } else { + return -1; + } +} + +/* Read bytes from a valid SSL connection on the pool. Blocking! */ +int ConnRead(int sslHandle, void *buf, int num) { + mbedtls_ssl_context* sslFD = NULL; + int ret; + + if (sslHandle >= 0) sslFD = sslHandles[sslHandle]; + else return -1; + + if (sslFD) { + if (num == 0) return 0; + ret = mbedtls_ssl_read(sslFD, buf, num); + return ret; + } else { + return -1; + } +} + +/* Write bytes to a valid SSL connection on the pool */ +int ConnWrite(int sslHandle, const void *buf, int num) { + mbedtls_ssl_context* sslFD = NULL; + int ret; + + if (sslHandle >= 0) sslFD = sslHandles[sslHandle]; + else return -1; + + if (sslFD) { + if (num == 0) return 0; + ret = mbedtls_ssl_write(sslFD, buf, num); + return ret; + } else { + return -1; + } +} + +/* Close a valid SSL connection on the pool, release the resources and +open the slot to another connection */ +void ConnClose(int sslHandle) { + mbedtls_ssl_context *sslFD = NULL; + + if (sslHandle >= 0) sslFD = sslHandles[sslHandle]; + else return; + + if (sslFD) { + mbedtls_ssl_close_notify( sslFD ); + + mbedtls_net_free( sslFD->p_bio ); + free(sslFD->p_bio); + + mbedtls_ssl_free( sslFD ); + free(sslFD); + } + + sslHandles[sslHandle] = NULL; +} + +void sleep_ms(int milliseconds) +{ + vTaskDelay(milliseconds / portTICK_RATE_MS); +} diff --git a/examples/websocket_mbedtls/conn.h b/examples/websocket_mbedtls/conn.h new file mode 100644 index 0000000..77d9fca --- /dev/null +++ b/examples/websocket_mbedtls/conn.h @@ -0,0 +1,11 @@ +#ifndef CONN +#define CONN + +int ConnConnect(char *host, int port); +int ConnReadBytesAvailable(int sslHandle); +int ConnRead(int sslHandle, void *buf, int num); +int ConnWrite(int sslHandle, const void *buf, int num); +void ConnClose(int sslHandle); +void sleep_ms(int milliseconds); + +#endif \ No newline at end of file diff --git a/examples/websocket_mbedtls/crc32.c b/examples/websocket_mbedtls/crc32.c new file mode 100644 index 0000000..fce7d33 --- /dev/null +++ b/examples/websocket_mbedtls/crc32.c @@ -0,0 +1,64 @@ +/* + * CRC32 checksum + * + * Copyright (c) 1998-2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +/* + * CRC32 algorithm taken from the zlib source, which is + * Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler + */ + +#include "tinf.h" + +static const unsigned int tinf_crc32tab[16] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, + 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, + 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, + 0xbdbdf21c +}; + +unsigned int tinf_crc32(const void *data, unsigned int length) +{ + const unsigned char *buf = (const unsigned char *)data; + unsigned int crc = 0xffffffff; + unsigned int i; + + if (length == 0) return 0; + + for (i = 0; i < length; ++i) + { + crc ^= buf[i]; + crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); + crc = tinf_crc32tab[crc & 0x0f] ^ (crc >> 4); + } + + return crc ^ 0xffffffff; +} diff --git a/examples/websocket_mbedtls/defl_static.c b/examples/websocket_mbedtls/defl_static.c new file mode 100644 index 0000000..f797847 --- /dev/null +++ b/examples/websocket_mbedtls/defl_static.c @@ -0,0 +1,308 @@ +/* + +Routines in this file are based on: +Zlib (RFC1950 / RFC1951) compression for PuTTY. + +PuTTY is copyright 1997-2014 Simon Tatham. + +Portions copyright Robert de Bath, Joris van Rantwijk, Delian +Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, +Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus +Kuhn, Colin Watson, and CORE SDI S.A. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation files +(the "Software"), to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include "defl_static.h" + +#define snew(type) ( (type *) malloc(sizeof(type)) ) +#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) ) +#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) ) +#define sfree(x) ( free((x)) ) + +#ifndef FALSE +#define FALSE 0 +#define TRUE (!FALSE) +#endif + +/* ---------------------------------------------------------------------- + * Zlib compression. We always use the static Huffman tree option. + * Mostly this is because it's hard to scan a block in advance to + * work out better trees; dynamic trees are great when you're + * compressing a large file under no significant time constraint, + * but when you're compressing little bits in real time, things get + * hairier. + * + * I suppose it's possible that I could compute Huffman trees based + * on the frequencies in the _previous_ block, as a sort of + * heuristic, but I'm not confident that the gain would balance out + * having to transmit the trees. + */ + +void outbits(struct Outbuf *out, unsigned long bits, int nbits) +{ + assert(out->noutbits + nbits <= 32); + out->outbits |= bits << out->noutbits; + out->noutbits += nbits; + while (out->noutbits >= 8) { + if (out->outlen >= out->outsize) { + out->outsize = out->outlen + 64; + out->outbuf = sresize(out->outbuf, out->outsize, unsigned char); + } + out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF); + out->outbits >>= 8; + out->noutbits -= 8; + } +} + +static const unsigned char mirrorbytes[256] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, +}; + +typedef struct { + short code, extrabits; + int min, max; +} coderecord; + +static const coderecord lencodes[] = { + {257, 0, 3, 3}, + {258, 0, 4, 4}, + {259, 0, 5, 5}, + {260, 0, 6, 6}, + {261, 0, 7, 7}, + {262, 0, 8, 8}, + {263, 0, 9, 9}, + {264, 0, 10, 10}, + {265, 1, 11, 12}, + {266, 1, 13, 14}, + {267, 1, 15, 16}, + {268, 1, 17, 18}, + {269, 2, 19, 22}, + {270, 2, 23, 26}, + {271, 2, 27, 30}, + {272, 2, 31, 34}, + {273, 3, 35, 42}, + {274, 3, 43, 50}, + {275, 3, 51, 58}, + {276, 3, 59, 66}, + {277, 4, 67, 82}, + {278, 4, 83, 98}, + {279, 4, 99, 114}, + {280, 4, 115, 130}, + {281, 5, 131, 162}, + {282, 5, 163, 194}, + {283, 5, 195, 226}, + {284, 5, 227, 257}, + {285, 0, 258, 258}, +}; + +static const coderecord distcodes[] = { + {0, 0, 1, 1}, + {1, 0, 2, 2}, + {2, 0, 3, 3}, + {3, 0, 4, 4}, + {4, 1, 5, 6}, + {5, 1, 7, 8}, + {6, 2, 9, 12}, + {7, 2, 13, 16}, + {8, 3, 17, 24}, + {9, 3, 25, 32}, + {10, 4, 33, 48}, + {11, 4, 49, 64}, + {12, 5, 65, 96}, + {13, 5, 97, 128}, + {14, 6, 129, 192}, + {15, 6, 193, 256}, + {16, 7, 257, 384}, + {17, 7, 385, 512}, + {18, 8, 513, 768}, + {19, 8, 769, 1024}, + {20, 9, 1025, 1536}, + {21, 9, 1537, 2048}, + {22, 10, 2049, 3072}, + {23, 10, 3073, 4096}, + {24, 11, 4097, 6144}, + {25, 11, 6145, 8192}, + {26, 12, 8193, 12288}, + {27, 12, 12289, 16384}, + {28, 13, 16385, 24576}, + {29, 13, 24577, 32768}, +}; + +void zlib_literal(struct Outbuf *out, unsigned char c) +{ + if (out->comp_disabled) { + /* + * We're in an uncompressed block, so just output the byte. + */ + outbits(out, c, 8); + return; + } + + if (c <= 143) { + /* 0 through 143 are 8 bits long starting at 00110000. */ + outbits(out, mirrorbytes[0x30 + c], 8); + } else { + /* 144 through 255 are 9 bits long starting at 110010000. */ + outbits(out, 1 + 2 * mirrorbytes[0x90 - 144 + c], 9); + } +} + +void zlib_match(struct Outbuf *out, int distance, int len) +{ + const coderecord *d, *l; + int i, j, k; + + assert(!out->comp_disabled); + + while (len > 0) { + int thislen; + + /* + * We can transmit matches of lengths 3 through 258 + * inclusive. So if len exceeds 258, we must transmit in + * several steps, with 258 or less in each step. + * + * Specifically: if len >= 261, we can transmit 258 and be + * sure of having at least 3 left for the next step. And if + * len <= 258, we can just transmit len. But if len == 259 + * or 260, we must transmit len-3. + */ + thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3); + len -= thislen; + + /* + * Binary-search to find which length code we're + * transmitting. + */ + i = -1; + j = sizeof(lencodes) / sizeof(*lencodes); + while (1) { + assert(j - i >= 2); + k = (j + i) / 2; + if (thislen < lencodes[k].min) + j = k; + else if (thislen > lencodes[k].max) + i = k; + else { + l = &lencodes[k]; + break; /* found it! */ + } + } + + /* + * Transmit the length code. 256-279 are seven bits + * starting at 0000000; 280-287 are eight bits starting at + * 11000000. + */ + if (l->code <= 279) { + outbits(out, mirrorbytes[(l->code - 256) * 2], 7); + } else { + outbits(out, mirrorbytes[0xc0 - 280 + l->code], 8); + } + + /* + * Transmit the extra bits. + */ + if (l->extrabits) + outbits(out, thislen - l->min, l->extrabits); + + /* + * Binary-search to find which distance code we're + * transmitting. + */ + i = -1; + j = sizeof(distcodes) / sizeof(*distcodes); + while (1) { + assert(j - i >= 2); + k = (j + i) / 2; + if (distance < distcodes[k].min) + j = k; + else if (distance > distcodes[k].max) + i = k; + else { + d = &distcodes[k]; + break; /* found it! */ + } + } + + /* + * Transmit the distance code. Five bits starting at 00000. + */ + outbits(out, mirrorbytes[d->code * 8], 5); + + /* + * Transmit the extra bits. + */ + if (d->extrabits) + outbits(out, distance - d->min, d->extrabits); + } +} + +void zlib_start_block(struct Outbuf *out) +{ +// outbits(out, 0x9C78, 16); + outbits(out, 1, 1); /* Final block */ + outbits(out, 1, 2); /* Static huffman block */ +} + +void zlib_finish_block(struct Outbuf *out) +{ + outbits(out, 0, 7); /* close block */ + outbits(out, 0, 7); /* Make sure all bits are flushed */ +} + +void zlib_free_block(struct Outbuf *out) { + sfree(out->outbuf); +} diff --git a/examples/websocket_mbedtls/defl_static.h b/examples/websocket_mbedtls/defl_static.h new file mode 100644 index 0000000..b3a349b --- /dev/null +++ b/examples/websocket_mbedtls/defl_static.h @@ -0,0 +1,14 @@ +struct Outbuf { + unsigned char *outbuf; + int outlen, outsize; + unsigned long outbits; + int noutbits; + int comp_disabled; +}; + +void outbits(struct Outbuf *out, unsigned long bits, int nbits); +void zlib_start_block(struct Outbuf *ctx); +void zlib_finish_block(struct Outbuf *ctx); +void zlib_literal(struct Outbuf *ectx, unsigned char c); +void zlib_match(struct Outbuf *ectx, int distance, int len); +void zlib_free_block(struct Outbuf *out); diff --git a/examples/websocket_mbedtls/genlz77.c b/examples/websocket_mbedtls/genlz77.c new file mode 100644 index 0000000..e68d400 --- /dev/null +++ b/examples/websocket_mbedtls/genlz77.c @@ -0,0 +1,116 @@ +/* + * genlz77 - Generic LZ77 compressor + * + * Copyright (c) 2014 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ +#include +#include +#include +#include "defl_static.h" + +#define HASH_BITS 10 +#define HASH_SIZE (1<> (3*8 - HASH_BITS)) - v) & (HASH_SIZE - 1); + return hash; +} + +#ifdef DUMP_LZTXT + +/* Counter for approximate compressed length in LZTXT mode. */ +/* Literal is counted as 1, copy as 2 bytes. */ +unsigned approx_compressed_len; + +void literal(void *data, uint8_t val) +{ + printf("L%02x # %c\n", val, (val >= 0x20 && val <= 0x7e) ? val : '?'); + approx_compressed_len++; +} + +void copy(void *data, unsigned offset, unsigned len) +{ + printf("C-%u,%u\n", offset, len); + approx_compressed_len += 2; +} + +#else + +static void literal(void *data, uint8_t val) +{ + zlib_literal(data, val); +} + +static void copy(void *data, unsigned offset, unsigned len) +{ + zlib_match(data, offset, len); +} + +#endif + + +void tinf_compress(void *data, const uint8_t *src, unsigned slen) +{ + const uint8_t *hashtable[HASH_SIZE] = {0}; + + const uint8_t *top = src + slen - MIN_MATCH; + while (src < top) { + int h = HASH(src); + const uint8_t **bucket = &hashtable[h & (HASH_SIZE - 1)]; + const uint8_t *subs = *bucket; + *bucket = src; + if (subs && src > subs && (src - subs) <= MAX_OFFSET && !memcmp(src, subs, MIN_MATCH)) { + src += MIN_MATCH; + const uint8_t *m = subs + MIN_MATCH; + int len = MIN_MATCH; + while (*src == *m && len < MAX_MATCH) { + src++; m++; len++; + } + copy(data, src - len - subs, len); + } else { + literal(data, *src++); + } + } + // Process buffer tail, which is less than MIN_MATCH + // (and so it doesn't make sense to look for matches there) + top += MIN_MATCH; + while (src < top) { + literal(data, *src++); + } +} diff --git a/examples/websocket_mbedtls/include/mbedtls/config.h b/examples/websocket_mbedtls/include/mbedtls/config.h new file mode 100644 index 0000000..2674292 --- /dev/null +++ b/examples/websocket_mbedtls/include/mbedtls/config.h @@ -0,0 +1,27 @@ +/* Special mbedTLS config file for http_get_mbedtls example, + overrides supported cipher suite list. + + Overriding the set of cipher suites saves small amounts of ROM and + RAM, and is a good practice in general if you know what server(s) + you want to connect to. + + However it's extra important here because the howsmyssl API sends + back the list of ciphers we send it as a JSON list in the, and we + only have a 4096kB receive buffer. If the server supported maximum + fragment length option then we wouldn't have this problem either, + but we do so this is a good workaround. + + The ciphers chosen below are common ECDHE ciphers, the same ones + Firefox uses when connecting to a TLSv1.2 server. +*/ +#ifndef MBEDTLS_CONFIG_H + +/* include_next picks up default config from extras/mbedtls/include/mbedtls/config.h */ +#include_next + +#define MBEDTLS_SSL_CIPHERSUITES MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,MBEDTLS_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + +/* uncomment next line to include debug output from example */ +//#define MBEDTLS_DEBUG_C + +#endif diff --git a/examples/websocket_mbedtls/tinf.h b/examples/websocket_mbedtls/tinf.h new file mode 100644 index 0000000..e9401f2 --- /dev/null +++ b/examples/websocket_mbedtls/tinf.h @@ -0,0 +1,102 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014 by Paul Sokolovsky + */ + +#ifndef TINF_H_INCLUDED +#define TINF_H_INCLUDED + +#include + +/* calling convention */ +#ifndef TINFCC + #ifdef __WATCOMC__ + #define TINFCC __cdecl + #else + #define TINFCC + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define TINF_OK 0 +#define TINF_DATA_ERROR (-3) +#define TINF_DEST_OVERFLOW (-4) + +/* data structures */ + +typedef struct { + unsigned short table[16]; /* table of code length counts */ + unsigned short trans[288]; /* code -> symbol translation table */ +} TINF_TREE; + +struct TINF_DATA; +typedef struct TINF_DATA { + const unsigned char *source; + unsigned int tag; + unsigned int bitcount; + + /* Buffer start */ + unsigned char *destStart; + /* Buffer total size */ + unsigned int destSize; + /* Current pointer in buffer */ + unsigned char *dest; + /* Remaining bytes in buffer */ + unsigned int destRemaining; + /* Argument is the allocation size which didn't fit into buffer. Note that + exact mimumum size to grow buffer by is lastAlloc - destRemaining. But + growing by this exact size is ineficient, as the next allocation will + fail again. */ + int (*destGrow)(struct TINF_DATA *data, unsigned int lastAlloc); + + TINF_TREE ltree; /* dynamic length/symbol tree */ + TINF_TREE dtree; /* dynamic distance tree */ +} TINF_DATA; + + +/* low-level API */ + +/* Step 1: Allocate TINF_DATA structure */ +/* Step 2: Set destStart, destSize, and destGrow fields */ +/* Step 3: Set source field */ +/* Step 4: Call tinf_uncompress_dyn() */ +/* Step 5: In response to destGrow callback, update destStart and destSize fields */ +/* Step 6: When tinf_uncompress_dyn() returns, buf.dest points to a byte past last uncompressed byte */ + +int TINFCC tinf_uncompress_dyn(TINF_DATA *d); +int TINFCC tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen); + +/* high-level API */ + +void TINFCC tinf_init(void); + +int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +int TINFCC tinf_gzip_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +int TINFCC tinf_zlib_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen); + +unsigned int TINFCC tinf_adler32(const void *data, unsigned int length); + +unsigned int TINFCC tinf_crc32(const void *data, unsigned int length); + +/* compression API */ + +void TINFCC tinf_compress(void *data, const uint8_t *src, unsigned slen); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TINF_H_INCLUDED */ diff --git a/examples/websocket_mbedtls/tinfgzip.c b/examples/websocket_mbedtls/tinfgzip.c new file mode 100644 index 0000000..1a16795 --- /dev/null +++ b/examples/websocket_mbedtls/tinfgzip.c @@ -0,0 +1,124 @@ +/* + * tinfgzip - tiny gzip decompressor + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +#define FTEXT 1 +#define FHCRC 2 +#define FEXTRA 4 +#define FNAME 8 +#define FCOMMENT 16 + +int tinf_gzip_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + unsigned char *src = (unsigned char *)source; + unsigned char *dst = (unsigned char *)dest; + unsigned char *start; + unsigned int dlen, crc32; + int res; + unsigned char flg; + + /* -- check format -- */ + + /* check id bytes */ + if (src[0] != 0x1f || src[1] != 0x8b) return TINF_DATA_ERROR; + + /* check method is deflate */ + if (src[2] != 8) return TINF_DATA_ERROR; + + /* get flag byte */ + flg = src[3]; + + /* check that reserved bits are zero */ + if (flg & 0xe0) return TINF_DATA_ERROR; + + /* -- find start of compressed data -- */ + + /* skip base header of 10 bytes */ + start = src + 10; + + /* skip extra data if present */ + if (flg & FEXTRA) + { + unsigned int xlen = start[1]; + xlen = 256*xlen + start[0]; + start += xlen + 2; + } + + /* skip file name if present */ + if (flg & FNAME) { while (*start) ++start; ++start; } + + /* skip file comment if present */ + if (flg & FCOMMENT) { while (*start) ++start; ++start; } + + /* check header crc if present */ + if (flg & FHCRC) + { + unsigned int hcrc = start[1]; + hcrc = 256*hcrc + start[0]; + + if (hcrc != (tinf_crc32(src, start - src) & 0x0000ffff)) + return TINF_DATA_ERROR; + + start += 2; + } + + /* -- get decompressed length -- */ + + dlen = src[sourceLen - 1]; + dlen = 256*dlen + src[sourceLen - 2]; + dlen = 256*dlen + src[sourceLen - 3]; + dlen = 256*dlen + src[sourceLen - 4]; + + /* -- get crc32 of decompressed data -- */ + + crc32 = src[sourceLen - 5]; + crc32 = 256*crc32 + src[sourceLen - 6]; + crc32 = 256*crc32 + src[sourceLen - 7]; + crc32 = 256*crc32 + src[sourceLen - 8]; + + /* -- decompress data -- */ + + res = tinf_uncompress(dst, destLen, start, src + sourceLen - start - 8); + + if (res != TINF_OK) return TINF_DATA_ERROR; + + if (*destLen != dlen) return TINF_DATA_ERROR; + + /* -- check CRC32 checksum -- */ + + if (crc32 != tinf_crc32(dst, dlen)) return TINF_DATA_ERROR; + + return TINF_OK; +} diff --git a/examples/websocket_mbedtls/tinflate.c b/examples/websocket_mbedtls/tinflate.c new file mode 100644 index 0000000..76db08c --- /dev/null +++ b/examples/websocket_mbedtls/tinflate.c @@ -0,0 +1,518 @@ +/* + * tinflate - tiny inflate + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +/* --------------------------------------------------- * + * -- uninitialized global data (static structures) -- * + * --------------------------------------------------- */ + +#ifdef RUNTIME_BITS_TABLES + +/* extra bits and base tables for length codes */ +unsigned char length_bits[30]; +unsigned short length_base[30]; + +/* extra bits and base tables for distance codes */ +unsigned char dist_bits[30]; +unsigned short dist_base[30]; + +#else + +const unsigned char length_bits[30] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5 +}; +const unsigned short length_base[30] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258 +}; + +const unsigned char dist_bits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; +const unsigned short dist_base[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, + 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577 +}; + +#endif + +/* special ordering of code length codes */ +const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +/* ----------------------- * + * -- utility functions -- * + * ----------------------- */ + +/* Execute callback to grow destination buffer */ +static int tinf_grow_dest_buf(TINF_DATA *d, unsigned int lastAlloc) +{ + unsigned int oldsize = d->dest - d->destStart; + + /* This will update only destStart and destSize */ + if (!d->destGrow) + { + return TINF_DEST_OVERFLOW; + } + + d->destGrow(d, lastAlloc); + d->dest = d->destStart + oldsize; + d->destRemaining = d->destSize - oldsize; + return 0; +} + +#ifdef RUNTIME_BITS_TABLES +/* build extra bits and base tables */ +static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) +{ + int i, sum; + + /* build bits table */ + for (i = 0; i < delta; ++i) bits[i] = 0; + for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; + + /* build base table */ + for (sum = first, i = 0; i < 30; ++i) + { + base[i] = sum; + sum += 1 << bits[i]; + } +} +#endif + +/* build the fixed huffman trees */ +static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt) +{ + int i; + + /* build fixed length tree */ + for (i = 0; i < 7; ++i) lt->table[i] = 0; + + lt->table[7] = 24; + lt->table[8] = 152; + lt->table[9] = 112; + + for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; + for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; + for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; + + /* build fixed distance tree */ + for (i = 0; i < 5; ++i) dt->table[i] = 0; + + dt->table[5] = 32; + + for (i = 0; i < 32; ++i) dt->trans[i] = i; +} + +/* given an array of code lengths, build a tree */ +static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) +{ + unsigned short offs[16]; + unsigned int i, sum; + + /* clear code length count table */ + for (i = 0; i < 16; ++i) t->table[i] = 0; + + /* scan symbol lengths, and sum code length counts */ + for (i = 0; i < num; ++i) t->table[lengths[i]]++; + + t->table[0] = 0; + + /* compute offset table for distribution sort */ + for (sum = 0, i = 0; i < 16; ++i) + { + offs[i] = sum; + sum += t->table[i]; + } + + /* create code->symbol translation table (symbols sorted by code) */ + for (i = 0; i < num; ++i) + { + if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + } +} + +/* ---------------------- * + * -- decode functions -- * + * ---------------------- */ + +/* get one bit from source stream */ +static int tinf_getbit(TINF_DATA *d) +{ + unsigned int bit; + + /* check if tag is empty */ + if (!d->bitcount--) + { + /* load next tag */ + d->tag = *d->source++; + d->bitcount = 7; + } + + /* shift bit out of tag */ + bit = d->tag & 0x01; + d->tag >>= 1; + + return bit; +} + +/* read a num bit value from a stream and add base */ +static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base) +{ + unsigned int val = 0; + + /* read num bits */ + if (num) + { + unsigned int limit = 1 << (num); + unsigned int mask; + + for (mask = 1; mask < limit; mask *= 2) + if (tinf_getbit(d)) val += mask; + } + + return val + base; +} + +/* given a data stream and a tree, decode a symbol */ +static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t) +{ + int sum = 0, cur = 0, len = 0; + + /* get more bits while code value is above sum */ + do { + + cur = 2*cur + tinf_getbit(d); + + ++len; + + sum += t->table[len]; + cur -= t->table[len]; + + } while (cur >= 0); + + return t->trans[sum + cur]; +} + +/* given a data stream, decode dynamic trees from it */ +static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + unsigned char lengths[288+32]; + unsigned int hlit, hdist, hclen; + unsigned int i, num, length; + + /* get 5 bits HLIT (257-286) */ + hlit = tinf_read_bits(d, 5, 257); + + /* get 5 bits HDIST (1-32) */ + hdist = tinf_read_bits(d, 5, 1); + + /* get 4 bits HCLEN (4-19) */ + hclen = tinf_read_bits(d, 4, 4); + + for (i = 0; i < 19; ++i) lengths[i] = 0; + + /* read code lengths for code length alphabet */ + for (i = 0; i < hclen; ++i) + { + /* get 3 bits code length (0-7) */ + unsigned int clen = tinf_read_bits(d, 3, 0); + + lengths[clcidx[i]] = clen; + } + + /* build code length tree, temporarily use length tree */ + tinf_build_tree(lt, lengths, 19); + + /* decode code lengths for the dynamic trees */ + for (num = 0; num < hlit + hdist; ) + { + int sym = tinf_decode_symbol(d, lt); + + switch (sym) + { + case 16: + /* copy previous code length 3-6 times (read 2 bits) */ + { + unsigned char prev = lengths[num - 1]; + for (length = tinf_read_bits(d, 2, 3); length; --length) + { + lengths[num++] = prev; + } + } + break; + case 17: + /* repeat code length 0 for 3-10 times (read 3 bits) */ + for (length = tinf_read_bits(d, 3, 3); length; --length) + { + lengths[num++] = 0; + } + break; + case 18: + /* repeat code length 0 for 11-138 times (read 7 bits) */ + for (length = tinf_read_bits(d, 7, 11); length; --length) + { + lengths[num++] = 0; + } + break; + default: + /* values 0-15 represent the actual code lengths */ + lengths[num++] = sym; + break; + } + } + + /* build dynamic trees */ + tinf_build_tree(lt, lengths, hlit); + tinf_build_tree(dt, lengths + hlit, hdist); +} + +/* ----------------------------- * + * -- block inflate functions -- * + * ----------------------------- */ + +/* given a stream and two trees, inflate a block of data */ +static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + while (1) + { + int sym = tinf_decode_symbol(d, lt); + + /* check for end of block */ + if (sym == 256) + { + return TINF_OK; + } + + if (sym < 256) + { + if (d->destRemaining == 0) + { + int res = tinf_grow_dest_buf(d, 1); + if (res) return res; + } + + *d->dest++ = sym; + d->destRemaining--; + + } else { + + unsigned int length, offs, i; + int dist; + + sym -= 257; + + /* possibly get more bits from length code */ + length = tinf_read_bits(d, length_bits[sym], length_base[sym]); + + dist = tinf_decode_symbol(d, dt); + + /* possibly get more bits from distance code */ + offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); + + if (d->destRemaining < length) + { + int res = tinf_grow_dest_buf(d, length); + if (res) return res; + } + + /* copy match */ + for (i = 0; i < length; ++i) + { + d->dest[i] = d->dest[(int)(i - offs)]; + } + + d->dest += length; + d->destRemaining -= length; + } + } +} + +/* inflate an uncompressed block of data */ +static int tinf_inflate_uncompressed_block(TINF_DATA *d) +{ + unsigned int length, invlength; + unsigned int i; + + /* get length */ + length = d->source[1]; + length = 256*length + d->source[0]; + + /* get one's complement of length */ + invlength = d->source[3]; + invlength = 256*invlength + d->source[2]; + + /* check length */ + if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; + + if (d->destRemaining < length) + { + int res = tinf_grow_dest_buf(d, length); + if (res) return res; + } + + d->source += 4; + + /* copy block */ + for (i = length; i; --i) *d->dest++ = *d->source++; + d->destRemaining -= length; + + /* make sure we start next block on a byte boundary */ + d->bitcount = 0; + + return TINF_OK; +} + +/* inflate a block of data compressed with fixed huffman trees */ +static int tinf_inflate_fixed_block(TINF_DATA *d) +{ + /* build fixed huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + + /* decode block using fixed trees */ + return tinf_inflate_block_data(d, &d->ltree, &d->dtree); +} + +/* inflate a block of data compressed with dynamic huffman trees */ +static int tinf_inflate_dynamic_block(TINF_DATA *d) +{ + /* decode trees from stream */ + tinf_decode_trees(d, &d->ltree, &d->dtree); + + /* decode block using decoded trees */ + return tinf_inflate_block_data(d, &d->ltree, &d->dtree); +} + +/* ---------------------- * + * -- public functions -- * + * ---------------------- */ + +/* initialize global (static) data */ +void tinf_init(void) +{ +#ifdef RUNTIME_BITS_TABLES + /* build extra bits and base tables */ + tinf_build_bits_base(length_bits, length_base, 4, 3); + tinf_build_bits_base(dist_bits, dist_base, 2, 1); + + /* fix a special case */ + length_bits[28] = 0; + length_base[28] = 258; +#endif +} + +/* inflate stream from source to dest */ +int tinf_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + (void)sourceLen; + TINF_DATA d; + int res; + + /* initialise data */ + d.source = (const unsigned char *)source; + + d.destStart = (unsigned char *)dest; + d.destRemaining = *destLen; + d.destSize = *destLen; + + res = tinf_uncompress_dyn(&d); + + *destLen = d.dest - d.destStart; + + return res; +} + +/* inflate stream from source to dest */ +int tinf_uncompress_dyn(TINF_DATA *d) +{ + int bfinal; + + /* initialise data */ + d->bitcount = 0; + + d->dest = d->destStart; + d->destRemaining = d->destSize; + + do { + + unsigned int btype; + int res; + + /* read final block flag */ + bfinal = tinf_getbit(d); + + /* read block type (2 bits) */ + btype = tinf_read_bits(d, 2, 0); + + /* decompress block */ + switch (btype) + { + case 0: + /* decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(d); + break; + case 1: + /* decompress block with fixed huffman trees */ + res = tinf_inflate_fixed_block(d); + break; + case 2: + /* decompress block with dynamic huffman trees */ + res = tinf_inflate_dynamic_block(d); + break; + default: + return TINF_DATA_ERROR; + } + + if (res != TINF_OK) return TINF_DATA_ERROR; + + } while (!bfinal); + + return TINF_OK; +} diff --git a/examples/websocket_mbedtls/tinfzlib.c b/examples/websocket_mbedtls/tinfzlib.c new file mode 100644 index 0000000..dbacc1d --- /dev/null +++ b/examples/websocket_mbedtls/tinfzlib.c @@ -0,0 +1,101 @@ +/* + * tinfzlib - tiny zlib decompressor + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * + * http://www.ibsensoftware.com/ + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +int tinf_zlib_uncompress(void *dest, unsigned int *destLen, + const void *source, unsigned int sourceLen) +{ + TINF_DATA d; + int res; + + /* initialise data */ + d.source = (const unsigned char *)source; + + d.destStart = (unsigned char *)dest; + d.destRemaining = *destLen; + + res = tinf_zlib_uncompress_dyn(&d, sourceLen); + + *destLen = d.dest - d.destStart; + + return res; +} + +int tinf_zlib_uncompress_dyn(TINF_DATA *d, unsigned int sourceLen) +{ + unsigned int a32; + int res; + unsigned char cmf, flg; + + /* -- get header bytes -- */ + + cmf = d->source[0]; + flg = d->source[1]; + + /* -- check format -- */ + + /* check checksum */ + if ((256*cmf + flg) % 31) return TINF_DATA_ERROR; + + /* check method is deflate */ + if ((cmf & 0x0f) != 8) return TINF_DATA_ERROR; + + /* check window size is valid */ + if ((cmf >> 4) > 7) return TINF_DATA_ERROR; + + /* check there is no preset dictionary */ + if (flg & 0x20) return TINF_DATA_ERROR; + + /* -- get adler32 checksum -- */ + + a32 = d->source[sourceLen - 4]; + a32 = 256*a32 + d->source[sourceLen - 3]; + a32 = 256*a32 + d->source[sourceLen - 2]; + a32 = 256*a32 + d->source[sourceLen - 1]; + + d->source += 2; + + /* -- inflate -- */ + + res = tinf_uncompress_dyn(d); + + if (res != TINF_OK) return res; + + /* -- check adler32 checksum -- */ + + if (a32 != tinf_adler32(d->destStart, d->dest - d->destStart)) return TINF_DATA_ERROR; + + return TINF_OK; +} + diff --git a/examples/websocket_mbedtls/util.h b/examples/websocket_mbedtls/util.h new file mode 100644 index 0000000..8d29b93 --- /dev/null +++ b/examples/websocket_mbedtls/util.h @@ -0,0 +1,28 @@ +// util +void hex_dump(const char *desc, const void *addr, const size_t len); + +#define Int32 signed long +#define Uint32 unsigned long +#define Byte unsigned char +#define Word unsigned short +#define Bool char +#define true 1 +#define false 0 + +#define MAKEWORD(a, b) ((Word)(((Byte)((a) & 0xff)) | ((Word)((Byte)((b) & 0xff))) << 8)) +#define MAKELONG(low,high) ((Int32)(((Word)(low)) | (((Uint32)((Word)(high))) << 16))) +#define LOWORD(l) ((Word)((l) & 0xffff)) +#define HIWORD(l) ((Word)((l) >> 16)) +#define LOBYTE(w) ((Byte)((w) & 0xff)) +#define HIBYTE(w) ((Byte)((w) >> 8)) +#define HH(x) HIBYTE(HIWORD( x )) +#define HL(x) LOBYTE(HIWORD( x )) +#define LH(x) HIBYTE(LOWORD( x )) +#define LL(x) LOBYTE(LOWORD( x )) +#define LONIBLE(x) (((Byte)x) & 0x0F ) +#define HINIBLE(x) ((((Byte)x) * 0xF0)>>4) + +#define _SWAPS(x) ((unsigned short)( \ + ((((unsigned short) x) & 0x000000FF) << 8) | \ + ((((unsigned short) x) & 0x0000FF00) >> 8) \ + )) diff --git a/examples/websocket_mbedtls/websocket_mbedtls.c b/examples/websocket_mbedtls/websocket_mbedtls.c new file mode 100644 index 0000000..68f5b94 --- /dev/null +++ b/examples/websocket_mbedtls/websocket_mbedtls.c @@ -0,0 +1,108 @@ +/* websocket_mbedtls - Websocket example using mbed TLS. + * + * It creates a websocket with a server running on Heroku. It uses TLS v1.2. + * I already implemented support to the permessage-deflate extension that + * reduces the footprint of the websocket frames using the inflate deflate + * algorithm. + + * The remaining memory when using the websocket on top of mbed TLS 1.2 is ~14Kb + * + * There is room for memory optimization. Feel free to improve the code. + * + * After get the esp8266 connected, go to ruby-websockets-chat.herokuapp.com + * and type some commands like break (to reconnect), turn led on and turn led off. + * + * If you wanna check the source code from the server: + * https://github.com/heroku-examples/ruby-websockets-chat-demo + * + * If you wanna work with the permessage-deflate extension, connect to the + * host serene-escarpment-15149.herokuapp.com and the path /echo . It is a websocket + * server echoing back your messages and supporting the permessage-deflate extension: + * https://github.com/luisbebop/websocket-echo-deflate + */ + +#include "espressif/esp_common.h" +#include "esp/uart.h" +#include +#include "FreeRTOS.h" +#include "task.h" +#include "ssid_config.h" +#include "conn.h" +#include "ws.h" +#include "util.h" + +const int gpio = 2; + +void websocket_task(void *pvParameters) +{ + //char * host = "serene-escarpment-15149.herokuapp.com"; + //char * path = "/echo"; + + char *host = "ruby-websockets-chat.herokuapp.com"; + char *path = "/"; + + int socket = 0, ret = 0; + int port = 443; + Bool compression = false, timeout = false; + char text[2048]; + + gpio_enable(gpio, GPIO_OUTPUT); + gpio_write(gpio, 1); + + while(1) { + vTaskDelay(5000 / portTICK_RATE_MS); + printf("top of loop, free heap = %u\n", xPortGetFreeHeapSize()); + + socket = ConnConnect(host, port); + printf("\nConnConnect socket %d\n", socket); + + ret = wsConnect(socket, host, path, &compression); + printf("wsConnect ret %d compression %d\n", ret, compression); + if ( ret < 0) + { + printf("websocket handshake error ret=%d\n", ret); + } + + strcpy(text, "{\"handle\":\"websocket-c-client\", \"text\": \"hello from low level ~*~ esp8266\"}"); + wsSendText(socket, text, compression); + + while(1) { + printf("top of loop, free heap = %u\n", xPortGetFreeHeapSize()); + + memset(text, 0, sizeof(text)); + wsReceiveText(socket, text, 2048, compression, &timeout, 15); + + if (timeout) { + printf("wsReceiveText timeout\n"); + } else { + printf("no timeout ...\n"); + printf("%s\n", text); + + if(strstr(text, "break") != 0) break; + if(strstr(text, "turn led on") != 0) gpio_write(gpio, 0); + if(strstr(text, "turn led off") != 0) gpio_write(gpio, 1); + } + } + + printf("Before ConnClose top of loop, free heap = %u\n", xPortGetFreeHeapSize()); + ConnClose(socket); + printf("After ConnClose top of loop, free heap = %u\n", xPortGetFreeHeapSize()); + } +} + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n", sdk_system_get_sdk_version()); + + struct sdk_station_config config = { + .ssid = WIFI_SSID, + .password = WIFI_PASS, + }; + + /* required to call wifi_set_opmode before station_set_config */ + sdk_wifi_set_opmode(STATION_MODE); + sdk_wifi_station_set_config(&config); + + xTaskCreate(&websocket_task, (signed char *)"websocket_task", 2048, NULL, 2, NULL); +} diff --git a/examples/websocket_mbedtls/ws.c b/examples/websocket_mbedtls/ws.c new file mode 100644 index 0000000..b0b4fe2 --- /dev/null +++ b/examples/websocket_mbedtls/ws.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#include "tinf.h" +#include "defl_static.h" +#include "conn.h" +#include "ws.h" + +const static char http_get[] = "GET wss://%s%s HTTP/1.1\r\n"; +const static char http_host[] = "Host: %s\r\n"; +const static char http_origin[] = "Origin: https://%s\r\n"; +const static char http_upgrade[] = "Upgrade: websocket\r\n"; +const static char http_connection[] = "Connection: Upgrade\r\n"; +const static char http_ws_key[] = "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Extensions: permessage-deflate\r\n"; +const static char http_ws_version[] = "Sec-WebSocket-Version: 13\r\n"; + +#define WS_OPCODE_CONTINUATION 0x00 +#define WS_OPCODE_TEXT 0x01 +#define WS_OPCODE_BINARY 0x02 +#define WS_OPCODE_CONECTION_CLOSE 0x08 +#define WS_OPCODE_PING 0x09 +#define WS_OPCODE_PONG 0x0A + +int wsConnect(int socket, const char *host, const char *path, Bool *compression) { + char server_reply[1024]; + char buffer[1024]; + + memset(server_reply, 0, sizeof(server_reply)); + *compression = false; + + memset(buffer, 0, sizeof(buffer)); + sprintf(buffer, http_get, host, path); + ConnWrite(socket, buffer, strlen(buffer)); + + memset(buffer, 0, sizeof(buffer)); + sprintf(buffer, http_host, host); + ConnWrite(socket, buffer, strlen(buffer)); + + memset(buffer, 0, sizeof(buffer)); + sprintf(buffer, http_origin, host); + ConnWrite(socket, buffer, strlen(buffer)); + + ConnWrite(socket, http_upgrade, strlen(http_upgrade)); + ConnWrite(socket, http_connection, strlen(http_connection)); + ConnWrite(socket, http_ws_key, strlen(http_ws_key)); + ConnWrite(socket, http_ws_version, strlen(http_ws_version)); + ConnWrite(socket, "\r\n", 2); + + if (ConnRead(socket, server_reply, 300) <= 0) { + return -1; + } + + if (strstr(server_reply, "HTTP/1.1 101 Switching Protocols") == NULL) { + return -1; + } + + if (strstr(server_reply, "permessage-deflate") != 0) { + *compression = true; + } + + return 1; +} + +void wsInitFrame(WsFrame *frame) { + frame->finFlag = 0; + frame->maskingFlag = 0; + frame->opcode = 0; + frame->payloadLenght = 0; + frame->maskingMap = 0; + frame->payload = 0; +} + +void wsCreateTextFrame(WsFrame *frame, const char *text) { + frame->finFlag = 1; + frame->maskingFlag = 1; + frame->opcode = WS_OPCODE_TEXT; + frame->payloadLenght = strlen(text); + frame->maskingMap = 0x00000000; + frame->payload = (uint8_t *)text; +} + +void wsSendFrame(int socket, WsFrame *frame) { + uint8_t finOpcode = (frame->finFlag) ? frame->opcode | 0x80 : frame->opcode; + uint8_t maskPayloadLength = (frame->maskingFlag) ? frame->payloadLenght | 0x80 : frame->payloadLenght; + unsigned short sizePayload; + + // todo support extended payloadLength + // The length of the "Payload data", in bytes: if 0-125, that is the + // payload length. If 126, the following 2 bytes interpreted as a + // 16-bit unsigned integer are the payload length. If 127, the + // following 8 bytes interpreted as a 64-bit unsigned integer (the + // most significant bit MUST be 0) are the payload length. Multibyte + // length quantities are expressed in network byte order. Note that + // in all cases, the minimal number of bytes MUST be used to encode + // the length, for example, the length of a 124-byte-long string + // can't be encoded as the sequence 126, 0, 124. The payload length + // is the length of the "Extension data" + the length of the + // "Application data". The length of the "Extension data" may be + // zero, in which case the payload length is the length of the + // "Application data". + // FE = 126 + if (frame->payloadLenght > 125) { + maskPayloadLength = 0xfe; + sizePayload = MAKEWORD(LL(frame->payloadLenght), LH(frame->payloadLenght)); + } + + + ConnWrite(socket, &finOpcode, 1); + ConnWrite(socket, &maskPayloadLength, 1); + + if (frame->payloadLenght > 125) { + sizePayload = _SWAPS(sizePayload); + ConnWrite(socket, &sizePayload, 2); + } + + ConnWrite(socket, &frame->maskingMap , 4); + ConnWrite(socket, frame->payload, frame->payloadLenght); +} + +void wsReceiveFrame(int socket, WsFrame *frame, Bool *timeout, int seconds) { + unsigned char inBuffer[2048]; + int receivedTotal = 0; + int receivedLength = 0; + int i = 0, ret = 0; + + *timeout = false; + memset(inBuffer, 0, sizeof(inBuffer)); + + while((ret = ConnReadBytesAvailable(socket)) == 0) { + i++; + sleep_ms(10); + if ((i > (seconds * 100)) && ret == 0) { + *timeout = true; + return; + } + } + + while((receivedLength = ConnRead(socket, inBuffer + receivedTotal, 2 - receivedTotal)) > 0 && receivedTotal < 2) { + receivedTotal += receivedLength; + } + + frame->finFlag = (inBuffer[0] & 0x80) >> 7; + frame->opcode = inBuffer[0] & 0x0F; + frame->maskingFlag = inBuffer[1] & 0x80; + frame->payloadLenght = MAKEWORD(inBuffer[1], 0x00); + + if (frame->payloadLenght > 125) { + receivedLength = ConnRead(socket, inBuffer, 2); + frame->payloadLenght = MAKEWORD(inBuffer[1], inBuffer[0]); + } + + while((receivedLength = ConnRead(socket, inBuffer + receivedTotal, frame->payloadLenght + 2 - receivedTotal)) > 0 && receivedTotal < frame->payloadLenght) { + receivedTotal += receivedLength; + if ((receivedTotal - 2) == frame->payloadLenght) break; + } + + memcpy(frame->payload, inBuffer + 2, receivedTotal - 2); + + if (frame->opcode == WS_OPCODE_PING) { + wsSendPong(socket, frame); + } +} + +unsigned char gzipBuffer[2048]; +void wsInflateFrame(WsFrame *frame) { + unsigned int outlen = 2048; + + memset(gzipBuffer, 0, sizeof(gzipBuffer)); + + // https://tools.ietf.org/html/draft-ietf-hybi-permessage-compression-28#page-22 + // 7.2.2 Decompression + frame->payload[frame->payloadLenght-1] = 0x01; + frame->payload[frame->payloadLenght] = 0x00; + frame->payload[frame->payloadLenght+1] = 0x00; + frame->payload[frame->payloadLenght+2] = 0xff; + frame->payload[frame->payloadLenght+3] = 0xff; + + tinf_uncompress(gzipBuffer, &outlen, frame->payload, frame->payloadLenght+4); + + memset(frame->payload, 0, frame->payloadLenght+3); + memcpy(frame->payload, gzipBuffer, outlen); +} + +void wsDeflateFrame(WsFrame *frame) { + struct Outbuf out = {0}; + memset(gzipBuffer, 0, sizeof(gzipBuffer)); + + //Deflate payload + zlib_start_block(&out); + tinf_compress(&out, frame->payload, frame->payloadLenght); + zlib_finish_block(&out); + + memcpy(gzipBuffer, out.outbuf, out.outlen); + + frame->finFlag = 0; + frame->opcode = 0xc1; + frame->payloadLenght = out.outlen; + frame->payload = (uint8_t *)gzipBuffer; + + zlib_free_block(&out); +} + +void wsSendPong(int socket, WsFrame *frame) { + frame->maskingFlag = 1; + frame->opcode = WS_OPCODE_PONG; + frame->payloadLenght = 0; + wsSendFrame(socket, frame); +} + +void wsSendText(int socket, const char *text, Bool compression) { + WsFrame frame; + wsInitFrame(&frame); + wsCreateTextFrame(&frame, text); + if (compression) wsDeflateFrame(&frame); + wsSendFrame(socket, &frame); +} + +void wsReceiveText(int socket, char *buffer, int bufferSize, Bool compression, Bool *timeout, int seconds) { + WsFrame frame; + wsInitFrame(&frame); + memset(buffer, 0, bufferSize); + frame.payload = (uint8_t *)buffer; + wsReceiveFrame(socket, &frame, timeout, seconds); + if (frame.opcode == WS_OPCODE_TEXT && compression) wsInflateFrame(&frame); + if (frame.opcode == WS_OPCODE_PONG) strcpy(buffer, "OPCODE_PING"); +} + +void hex_dump(const char *desc, const void *addr, const size_t len) { + int i; + unsigned char buff[17]; + unsigned char *pc = (unsigned char*)addr; + + // Output description if given. + if (desc != NULL) + printf ("%s:\n", desc); + + // Process every byte in the data. + for (i = 0; i < len; i++) { + // Multiple of 16 means new line (with line offset). + + if ((i % 16) == 0) { + // Just don't print ASCII for the zeroth line. + if (i != 0) + printf (" %s\n", buff); + + // Output the offset. + printf (" %04x ", i); + } + + // Now the hex code for the specific character. + printf (" %02x", pc[i]); + + // And store a printable ASCII character for later. + if ((pc[i] < 0x20) || (pc[i] > 0x7e)) + buff[i % 16] = '.'; + else + buff[i % 16] = pc[i]; + buff[(i % 16) + 1] = '\0'; + } + + // Pad out last line if not exactly 16 characters. + while ((i % 16) != 0) { + printf (" "); + i++; + } + + // And print the final ASCII bit. + printf (" %s\n", buff); +} diff --git a/examples/websocket_mbedtls/ws.h b/examples/websocket_mbedtls/ws.h new file mode 100644 index 0000000..0aa1239 --- /dev/null +++ b/examples/websocket_mbedtls/ws.h @@ -0,0 +1,27 @@ +#ifndef WEBSOCKETS +#define WEBSOCKETS + +#include "util.h" + +typedef struct WsFrame_ { + Byte finFlag; + Byte maskingFlag; + Byte opcode; + Uint32 payloadLenght; + Uint32 maskingMap; + Byte *payload; +} WsFrame; + +int wsConnect(int socket, const char *host, const char *path, Bool *compression); +void wsInitFrame(WsFrame *frame); +void wsCreateTextFrame(WsFrame *frame, const char *text); +void wsSendFrame(int socket, WsFrame *frame); +void wsReceiveFrame(int socket, WsFrame *frame, Bool *timeout, int seconds); +void wsInflateFrame(WsFrame *frame); +void wsDeflateFrame(WsFrame *frame); +void wsSendPong(int socket, WsFrame *frame); +void wsSendText(int socket, const char *text, Bool compression); +void wsReceiveText(int socket, char *buffer, int bufferSize, Bool compression, Bool *timeout, int seconds); +void hex_dump(const char *desc, const void *addr, const size_t len); + +#endif diff --git a/lwip/include/lwipopts.h b/lwip/include/lwipopts.h index 8f46c7d..113c89d 100644 --- a/lwip/include/lwipopts.h +++ b/lwip/include/lwipopts.h @@ -345,7 +345,8 @@ /** * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. */ -#define LWIP_SO_RCVBUF 0 +#define LWIP_SO_RCVBUF 1 +#define INT_MAX __INT_MAX__ /** * SO_REUSE==1: Enable SO_REUSEADDR option.