Add websocket example that supports permessage-deflate extension

This commit is contained in:
luisbebop 2016-06-04 16:36:16 -03:00
parent 71f4609cb5
commit 145479f095
19 changed files with 2175 additions and 1 deletions

View file

@ -0,0 +1,4 @@
PROGRAM=websocket_mbedtls
COMPONENTS = FreeRTOS lwip core extras/mbedtls
include ../../common.mk

View file

@ -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;
}

View file

@ -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 </dev/null
The CA cert is the last cert in the chain output by the server.
*/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/*
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";

View file

@ -0,0 +1,229 @@
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include <string.h>
#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 <stdio.h>
#include <string.h>
#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);
}

View file

@ -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

View file

@ -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;
}

View file

@ -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 <stdlib.h>
#include <string.h>
#include <assert.h>
#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);
}

View file

@ -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);

View file

@ -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 <stdint.h>
#include <string.h>
#include <stdio.h>
#include "defl_static.h"
#define HASH_BITS 10
#define HASH_SIZE (1<<HASH_BITS)
/* Minimum and maximum length of matches to look for, inclusive */
#define MIN_MATCH 3
#define MAX_MATCH 258
/* Max offset of the match to look for, inclusive */
#define MAX_OFFSET 32768
/* Hash function can be defined as macro or as inline function */
/*#define HASH(p) (p[0] + p[1] + p[2])*/
/* This is hash function from liblzf */
static int HASH(const uint8_t *p) {
int v = (p[0] << 16) | (p[1] << 8) | p[2];
int hash = ((v >> (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++);
}
}

View file

@ -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<mbedtls/config.h>
#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

View file

@ -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 <stdint.h>
/* 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 */

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) \
))

View file

@ -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 <string.h>
#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);
}

View file

@ -0,0 +1,270 @@
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#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);
}

View file

@ -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

View file

@ -345,7 +345,8 @@
/** /**
* LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing. * 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. * SO_REUSE==1: Enable SO_REUSEADDR option.