mdebug

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

commit b23181fe9ed44b4cd3304332732d592ac74c7849
parent f37069ba3613aee8014716f3adb490e041a94477
Author: Brian Swetland <swetland@frotz.net>
Date:   Thu,  6 Aug 2015 16:39:41 -0700

debugger: make SWO data available via raw (2332) and web (5557) socket

Diffstat:
MMakefile | 3+++
Atools/base64.c | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/debugger-core.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mtools/debugger.h | 1+
Mtools/module.mk | 7+++++--
Mtools/rswdp.c | 2++
Atools/sha1.c | 307+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/sha1.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/swo.c | 4++--
Atools/websocket.c | 238+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/websocket.h | 33+++++++++++++++++++++++++++++++++
11 files changed, 878 insertions(+), 8 deletions(-)

diff --git a/Makefile b/Makefile @@ -20,6 +20,9 @@ SRCS := tools/debugger.c \ tools/rswdp.c \ tools/socket.c \ tools/swo.c \ + tools/websocket.c \ + tools/base64.c \ + tools/sha1.c \ tools/usb.c ifneq ($(TOOLCHAIN),) diff --git a/tools/base64.c b/tools/base64.c @@ -0,0 +1,126 @@ +/* base64.h + * + * Copyright 2011 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <unistd.h> +#include <string.h> + +typedef uint8_t u8; +typedef uint32_t u32; + +#define OP_MASK 0xC0 +#define OP_BITS 0x00 +#define OP_END 0x40 +#define OP_SKIP 0x80 +#define OP_BAD 0xC0 + +static u8 DTABLE[256] = + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x80\x80\xc0\xc0\x80\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x3e\xc0\xc0\xc0\x3f" + "\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\xc0\xc0\xc0\x40\xc0\xc0" + "\xc0\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" + "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\xc0\xc0\xc0\xc0\xc0" + "\xc0\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28" + "\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0" + "\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0"; + +static u8 ETABLE[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +int base64_decode(u8 *data, u32 len, u8 *out) +{ + u8 *start = out; + u32 tmp = 0, pos = 0; + + while (len-- > 0) { + u8 x = DTABLE[*data++]; + switch (x & OP_MASK) { + case OP_BITS: + tmp = (tmp << 6) | x; + if (++pos == 4) { + *out++ = tmp >> 16; + *out++ = tmp >> 8; + *out++ = tmp; + tmp = 0; + pos = 0; + } + break; + case OP_SKIP: + break; + case OP_END: + if (pos == 2) { + if (*data != '=') + return -1; + *out++ = (tmp >> 4); + } else if (pos == 3) { + *out++ = (tmp >> 10); + *out++ = (tmp >> 2); + } else { + return -1; + } + return out - start; + case OP_BAD: + return -1; + } + } + + if (pos != 0) + return -1; + + return out - start; +} + +int base64_encode(u8 *data, u32 len, u8 *out) +{ + u8 *start = out; + u32 n; + + while (len >= 3) { + n = (data[0] << 16) | (data[1] << 8) | data[2]; + data += 3; + len -= 3; + *out++ = ETABLE[(n >> 18) & 63]; + *out++ = ETABLE[(n >> 12) & 63]; + *out++ = ETABLE[(n >> 6) & 63]; + *out++ = ETABLE[n & 63]; + } + if (len == 2) { + *out++ = ETABLE[(data[0] >> 2) & 63]; + *out++ = ETABLE[((data[0] << 4) | (data[1] >> 4)) & 63]; + *out++ = ETABLE[(data[1] << 2) & 63]; + *out++ = '='; + } else if (len == 1) { + *out++ = ETABLE[(data[0] >> 2) & 63]; + *out++ = ETABLE[(data[0] << 4) & 63]; + *out++ = '='; + *out++ = '='; + } + + return out - start; +} diff --git a/tools/debugger-core.c b/tools/debugger-core.c @@ -22,6 +22,7 @@ #include <string.h> #include <ctype.h> #include <stdarg.h> +#include <errno.h> #include <pthread.h> #include <sys/socket.h> @@ -31,6 +32,8 @@ #include "debugger.h" #include "rswdp.h" +#include "websocket.h" + #define DHCSR_C_DEBUGEN (1 << 0) #define DHCSR_C_HALT (1 << 1) #define DHCSR_C_STEP (1 << 2) @@ -53,6 +56,10 @@ extern int swd_verbose; +#define GDB_SOCKET 5555 +#define SWO_SOCKET 2332 +#define WEB_SOCKET 5557 + static void m_event(const char *evt) { xprintf(XCORE, "DEBUG EVENT: %s\n", evt); } @@ -74,6 +81,13 @@ static void monitor(void) { static pthread_mutex_t _dbg_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_t _dbg_thread; +static pthread_t _gdb_thread; + +static pthread_mutex_t _swo_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_t _swo_thread; +static pthread_t _ws_thread; +static int swo_fd = -1; +static ws_server_t *_ws = NULL; void debugger_lock() { pthread_mutex_lock(&_dbg_lock); @@ -109,11 +123,10 @@ void *debugger_monitor(void *arg) { void gdb_server(int fd); int socket_listen_tcp(unsigned port); -static pthread_t _listen_master; void *gdb_listener(void *arg) { int fd; - if ((fd = socket_listen_tcp(5555)) < 0) { - xprintf(XGDB, "gdb_listener() cannot bind to 5555\n"); + if ((fd = socket_listen_tcp(GDB_SOCKET)) < 0) { + xprintf(XGDB, "gdb_listener() cannot bind to %d\n", GDB_SOCKET); return NULL; } for (;;) { @@ -126,9 +139,90 @@ void *gdb_listener(void *arg) { return NULL; } +void transmit_swo_data(void *data, unsigned len) { + pthread_mutex_lock(&_swo_lock); + if (swo_fd >= 0) { + if (write(swo_fd, data, len)) ; + } + if (_ws != NULL) { + unsigned char *x = data; + x--; + *x = 3; // SWO DATA MARKER + ws_send_binary(_ws, x, len + 1); + } + pthread_mutex_unlock(&_swo_lock); +} + +void *swo_listener(void *arg) { + char buf[1024]; + int fd; + if ((fd = socket_listen_tcp(SWO_SOCKET)) < 0) { + xprintf(XCORE, "swo_listener() cannot bind to %d\n", SWO_SOCKET); + return NULL; + } + for (;;) { + int s = accept(fd, NULL, NULL); + if (s >= 0) { + xprintf(XCORE, "[ swo listener connected ]\n"); + pthread_mutex_lock(&_swo_lock); + swo_fd = s; + pthread_mutex_unlock(&_swo_lock); + for (;;) { + int r = read(s, buf, 1024); + if (r < 0) { + if (errno != EINTR) break; + } + if (r == 0) break; + } + pthread_mutex_lock(&_swo_lock); + swo_fd = -1; + pthread_mutex_unlock(&_swo_lock); + close(s); + xprintf(XCORE, "[ swo listener disconnected ]\n"); + } + } + return NULL; +} + +static void ws_message(unsigned op, void *msg, size_t len, void *cookie) { +} + +void *ws_listener(void *arg) { + ws_server_t *ws; + int fd; + if ((fd = socket_listen_tcp(WEB_SOCKET)) < 0) { + xprintf(XCORE, "websocket cannot bind to %d\n", WEB_SOCKET); + return NULL; + } + for (;;) { + int s = accept(fd, NULL, NULL); + if (s >= 0) { + ws = ws_handshake(s, ws_message, NULL); + if (ws) { + xprintf(XCORE, "[ websocket connected ]\n"); + } else { + xprintf(XCORE, "[ websocket handshake failed ]\n"); + continue; + } + pthread_mutex_lock(&_swo_lock); + _ws = ws; + pthread_mutex_unlock(&_swo_lock); + ws_process_messages(ws); + pthread_mutex_lock(&_swo_lock); + _ws = NULL; + pthread_mutex_unlock(&_swo_lock); + ws_close(ws); + xprintf(XCORE, "[ websocket disconnected ]\n"); + } + } + return NULL; +} + void debugger_init() { pthread_create(&_dbg_thread, NULL, debugger_monitor, NULL); - pthread_create(&_listen_master, NULL, gdb_listener, NULL); + pthread_create(&_gdb_thread, NULL, gdb_listener, NULL); + pthread_create(&_swo_thread, NULL, swo_listener, NULL); + pthread_create(&_ws_thread, NULL, ws_listener, NULL); } diff --git a/tools/debugger.h b/tools/debugger.h @@ -24,6 +24,7 @@ typedef enum { XCORE, // debugger core XDATA, // debugger command response XGDB, // messages from GDB bridge + XREMOTE, // remote console messages } xpchan; #define printf __use_xprintf_in_debugger__ diff --git a/tools/module.mk b/tools/module.mk @@ -11,12 +11,15 @@ M_OBJS += tools/socket.o M_OBJS += tools/gdb-bridge.o M_OBJS += tools/lkdebug.o M_OBJS += tools/swo.o +M_OBJS += tools/websocket.o +M_OBJS += tools/sha1.o +M_OBJS += tools/base64.o M_OBJS += out/debugger-builtins.o $(call build-host-executable) out/debugger-builtins.c: $(AGENTS) bin/mkbuiltins @mkdir -p out - ./bin/mkbuiltins $(AGENTS) > $@ + ./bin/mkbuiltins $(AGENTS) > $@ M_NAME := stm32boot M_OBJS := tools/stm32boot.o @@ -40,7 +43,7 @@ M_NAME := lpcboot M_OBJS := tools/lpcboot.o tools/usb.o $(call build-host-executable) -M_NAME := uconsole +M_NAME := uconsole M_OBJS := tools/uconsole.o tools/usb.o $(call build-host-executable) diff --git a/tools/rswdp.c b/tools/rswdp.c @@ -92,6 +92,7 @@ struct txn { }; void process_swo_data(void *data, unsigned count); +void transmit_swo_data(void *data, unsigned count); static void process_async(u32 *data, unsigned count) { unsigned msg, n; @@ -117,6 +118,7 @@ static void process_async(u32 *data, unsigned count) { if (n > count) return; process_swo_data(data, n * 4); + transmit_swo_data(data, n * 4); data += n; count -= n; default: diff --git a/tools/sha1.c b/tools/sha1.c @@ -0,0 +1,307 @@ +/* sha.c +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "sha1.h" + +// Some machines lack byteswap.h and endian.h. These have to use the +// slower code, even if they're little-endian. + +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + +#include <byteswap.h> +#include <memory.h> + +// This version is about 28% faster than the generic version below, +// but assumes little-endianness. + +static inline uint32_t ror27(uint32_t val) { + return (val >> 27) | (val << 5); +} +static inline uint32_t ror2(uint32_t val) { + return (val >> 2) | (val << 30); +} +static inline uint32_t ror31(uint32_t val) { + return (val >> 31) | (val << 1); +} + +static void SHA1_Transform(SHA_CTX* ctx) { + uint32_t W[80]; + register uint32_t A, B, C, D, E; + int t; + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = bswap_32(ctx->buf.w[t])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + for (t = 0; t < 15; t += 5) { + SHA_F1(A,B,C,D,E,t + 0); + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + } + SHA_F1(A,B,C,D,E,t + 0); // 16th one, t == 15 + +#undef SHA_F1 + +#define SHA_F1(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (D^(B&(C^D))) + 0x5A827999; \ + B = ror2(B); + + SHA_F1(E,A,B,C,D,t + 1); + SHA_F1(D,E,A,B,C,t + 2); + SHA_F1(C,D,E,A,B,t + 3); + SHA_F1(B,C,D,E,A,t + 4); + +#undef SHA_F1 + +#define SHA_F2(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0x6ED9EBA1; \ + B = ror2(B); + + for (t = 20; t < 40; t += 5) { + SHA_F2(A,B,C,D,E,t + 0); + SHA_F2(E,A,B,C,D,t + 1); + SHA_F2(D,E,A,B,C,t + 2); + SHA_F2(C,D,E,A,B,t + 3); + SHA_F2(B,C,D,E,A,t + 4); + } + +#undef SHA_F2 + +#define SHA_F3(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + ((B&C)|(D&(B|C))) + 0x8F1BBCDC; \ + B = ror2(B); + + for (; t < 60; t += 5) { + SHA_F3(A,B,C,D,E,t + 0); + SHA_F3(E,A,B,C,D,t + 1); + SHA_F3(D,E,A,B,C,t + 2); + SHA_F3(C,D,E,A,B,t + 3); + SHA_F3(B,C,D,E,A,t + 4); + } + +#undef SHA_F3 + +#define SHA_F4(A,B,C,D,E,t) \ + E += ror27(A) + \ + (W[t] = ror31(W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16])) + \ + (B^C^D) + 0xCA62C1D6; \ + B = ror2(B); + + for (; t < 80; t += 5) { + SHA_F4(A,B,C,D,E,t + 0); + SHA_F4(E,A,B,C,D,t + 1); + SHA_F4(D,E,A,B,C,t + 2); + SHA_F4(C,D,E,A,B,t + 3); + SHA_F4(B,C,D,E,A,t + 4); + } + +#undef SHA_F4 + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA_update(SHA_CTX* ctx, const void* data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len > sizeof(ctx->buf) - i) { + memcpy(&ctx->buf.b[i], p, sizeof(ctx->buf) - i); + len -= sizeof(ctx->buf) - i; + p += sizeof(ctx->buf) - i; + SHA1_Transform(ctx); + i = 0; + } + + while (len--) { + ctx->buf.b[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_Transform(ctx); + i = 0; + } + } +} + + +const uint8_t* SHA_final(SHA_CTX* ctx) { + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + ctx->buf.w[i] = bswap_32(ctx->state[i]); + } + + return ctx->buf.b; +} + +#else // #if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + +#define rol(bits, value) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +static void SHA1_transform(SHA_CTX *ctx) { + uint32_t W[80]; + uint32_t A, B, C, D, E; + uint8_t *p = ctx->buf; + int t; + + for(t = 0; t < 16; ++t) { + uint32_t tmp = *p++ << 24; + tmp |= *p++ << 16; + tmp |= *p++ << 8; + tmp |= *p++; + W[t] = tmp; + } + + for(; t < 80; t++) { + W[t] = rol(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = ctx->state[0]; + B = ctx->state[1]; + C = ctx->state[2]; + D = ctx->state[3]; + E = ctx->state[4]; + + for(t = 0; t < 80; t++) { + uint32_t tmp = rol(5,A) + E + W[t]; + + if (t < 20) + tmp += (D^(B&(C^D))) + 0x5A827999; + else if ( t < 40) + tmp += (B^C^D) + 0x6ED9EBA1; + else if ( t < 60) + tmp += ((B&C)|(D&(B|C))) + 0x8F1BBCDC; + else + tmp += (B^C^D) + 0xCA62C1D6; + + E = D; + D = C; + C = rol(30,B); + B = A; + A = tmp; + } + + ctx->state[0] += A; + ctx->state[1] += B; + ctx->state[2] += C; + ctx->state[3] += D; + ctx->state[4] += E; +} + +void SHA_update(SHA_CTX *ctx, const void *data, int len) { + int i = ctx->count % sizeof(ctx->buf); + const uint8_t* p = (const uint8_t*)data; + + ctx->count += len; + + while (len--) { + ctx->buf[i++] = *p++; + if (i == sizeof(ctx->buf)) { + SHA1_transform(ctx); + i = 0; + } + } +} +const uint8_t *SHA_final(SHA_CTX *ctx) { + uint8_t *p = ctx->buf; + uint64_t cnt = ctx->count * 8; + int i; + + SHA_update(ctx, (uint8_t*)"\x80", 1); + while ((ctx->count % sizeof(ctx->buf)) != (sizeof(ctx->buf) - 8)) { + SHA_update(ctx, (uint8_t*)"\0", 1); + } + for (i = 0; i < 8; ++i) { + uint8_t tmp = cnt >> ((7 - i) * 8); + SHA_update(ctx, &tmp, 1); + } + + for (i = 0; i < 5; i++) { + uint32_t tmp = ctx->state[i]; + *p++ = tmp >> 24; + *p++ = tmp >> 16; + *p++ = tmp >> 8; + *p++ = tmp >> 0; + } + + return ctx->buf; +} + +#endif // endianness + +void SHA_init(SHA_CTX* ctx) { + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xEFCDAB89; + ctx->state[2] = 0x98BADCFE; + ctx->state[3] = 0x10325476; + ctx->state[4] = 0xC3D2E1F0; + ctx->count = 0; +} + +/* Convenience function */ +const uint8_t* SHA(const void *data, int len, uint8_t *digest) { + const uint8_t *p; + int i; + SHA_CTX ctx; + SHA_init(&ctx); + SHA_update(&ctx, data, len); + p = SHA_final(&ctx); + for (i = 0; i < SHA_DIGEST_SIZE; ++i) { + digest[i] = *p++; + } + return digest; +} diff --git a/tools/sha1.h b/tools/sha1.h @@ -0,0 +1,63 @@ +/* sha.h +** +** Copyright 2008, The Android Open Source Project +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** * Neither the name of Google Inc. nor the names of its contributors may +** be used to endorse or promote products derived from this software +** without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _EMBEDDED_SHA_H_ +#define _EMBEDDED_SHA_H_ + +#include <inttypes.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SHA_CTX { + uint64_t count; + uint32_t state[5]; +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) + union { + uint8_t b[64]; + uint32_t w[16]; + } buf; +#else + uint8_t buf[64]; +#endif +} SHA_CTX; + +void SHA_init(SHA_CTX* ctx); +void SHA_update(SHA_CTX* ctx, const void* data, int len); +const uint8_t* SHA_final(SHA_CTX* ctx); + +/* Convenience method. Returns digest parameter value. */ +const uint8_t* SHA(const void* data, int len, uint8_t* digest); + +#define SHA_DIGEST_SIZE 20 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/swo.c b/tools/swo.c @@ -37,11 +37,11 @@ static void handle_swv_src(unsigned id, unsigned val, unsigned n) { console_data[console_ptr++] = val; if (val == '\n') { console_data[console_ptr] = 0; - xprintf(XDATA, "[remote] %s", console_data); + xprintf(XREMOTE, "[remote] %s", console_data); console_ptr = 0; } else if (console_ptr == 254) { console_data[console_ptr] = 0; - xprintf(XDATA, "[remote] %s\n", console_data); + xprintf(XREMOTE, "[remote] %s\n", console_data); } } } diff --git a/tools/websocket.c b/tools/websocket.c @@ -0,0 +1,238 @@ +/* websocket.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include "sha1.h" +#include "websocket.h" + +struct ws_server { + FILE *fp; + int fd; + ws_callback_t func; + void *cookie; +}; + +int base64_decode(uint8_t *data, uint32_t len, uint8_t *out); +int base64_encode(uint8_t *data, uint32_t len, uint8_t *out); + +static const char *wsmagic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + +#define F_FIN 0x80 +#define F_INVAL 0x70 +#define OP_NONE 0 +#define OP_TEXT 1 +#define OP_BINARY 2 +#define OP_CLOSE 8 +#define OP_PING 9 +#define OP_PONG 10 + +#define F_MASKED 0x80 +#define LEN_16BIT 0x7E +#define LEN_64BIT 0x7F + +static int ws_send(int fd, unsigned op, const void *data, size_t len); + +int ws_send_text(ws_server_t *ws, const void *data, size_t len) { + return ws_send(ws->fd, OP_TEXT, data, len); +} + +int ws_send_binary(ws_server_t *ws, const void *data, size_t len) { + return ws_send(ws->fd, OP_BINARY, data, len); +} + +static int _ws_handshake(ws_server_t *ws) { + FILE *fp = ws->fp; + unsigned check = 0; + char line[8192 + 128]; + uint8_t digest[SHA_DIGEST_SIZE]; + int n; + + for (;;) { + if (fgets(line, sizeof(line), fp) == NULL) { + return -1; + } + if ((n = strlen(line)) == 0) { + return -1; + } + if (line[n-1] == '\n') { + line[n-1] = 0; + if ((n > 1) && (line[n-2] == '\r')) { + line[n-2] = 0; + } + } + if (line[0] == 0) { + //printf(">BREAK<\n"); + break; + } + //printf(">HEADER: %s<\n", line); + if (!strcasecmp(line, "upgrade: websocket")) { + check |= 1; + continue; + } + if (!strcasecmp(line, "connection: upgrade")) { + // may be keep-alive, etc + check |= 2; + continue; + } + if (!strncasecmp(line, "sec-websocket-key: ", 19)) { + check |= 4; + strcat(line, wsmagic); + SHA(line + 19, strlen(line + 19), digest); + } + if (!strcasecmp(line, "sec-websocket-version: 13")) { + check |= 8; + } + // Host: + // Origin: + // Sec-WebSocket-Protocol: + // Sec-WebSocket-Extensions: + } + if ((check & 13) != 13) { + return -1; + } + n = base64_encode((void*) digest, SHA_DIGEST_SIZE, (void*) line); + line[n] = 0; + fprintf(fp, + "HTTP/1.1 101 Switching Protocols\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Sec-Websocket-Accept: %s\r\n" + "\r\n", line + ); + return 0; +} + +int ws_process_messages(ws_server_t *ws) { + FILE *fp = ws->fp; + unsigned char hdr[2+2+4]; + unsigned char msg[65536]; + unsigned len; + for (;;) { + if (fread(hdr, 2, 1, fp) != 1) break; + // client must not set invalid flags + if (hdr[0] & F_INVAL) break; + // client must mask + if (!(hdr[1] & F_MASKED)) break; + // todo: fragments + if (!(hdr[0] & F_FIN)) break; + // todo: large packets + if ((hdr[1] & 0x7F) == LEN_64BIT) break; + if ((hdr[1] & 0x7F) == LEN_16BIT) { + if (fread(hdr + 2, 6, 1, fp) != 1) break; + len = (hdr[2] << 8) | hdr[3]; + } else { + if (fread(hdr + 4, 4, 1, fp) != 1) break; + len = hdr[1] & 0x7F; + } +#if 0 + printf("<op %d len %d mask %02x%02x%02x%02x>\n", + hdr[0] & 15, len, hdr[4], hdr[5], hdr[6], hdr[7]); +#endif + if (fread(msg, len, 1, fp) != 1) break; + // op? +#if 0 + unsigned i; + for (i = 0; i < len; i++) { + msg[i] ^= hdr[4 + (i & 3)]; + printf("%02x ", msg[i]); + } + printf("\n"); +#endif + switch (hdr[0] & 15) { + case OP_PING: + break; + case OP_PONG: + break; + case OP_CLOSE: + // todo: reply + return 0; + case OP_TEXT: + ws->func(OP_TEXT, msg, len, ws->cookie); + break; + case OP_BINARY: + ws->func(OP_BINARY, msg, len, ws->cookie); + break; + default: + // invalid opcode + return 0; + } + } + return 0; +} + +static int ws_send(int fd, unsigned op, const void *data, size_t len) { + unsigned char hdr[4]; + struct iovec iov[2]; + if (len > 65535) return -1; + hdr[0] = F_FIN | (op & 15); + if (len < 126) { + hdr[1] = len; + iov[0].iov_len = 2; + } else { + hdr[1] = LEN_16BIT; + hdr[2] = len >> 8; + hdr[3] = len; + iov[0].iov_len = 4; + } + iov[0].iov_base = hdr; + iov[1].iov_base = (void*) data; + iov[1].iov_len = len; + return writev(fd, iov, 2); +} + +ws_server_t *ws_handshake(int fd, ws_callback_t func, void *cookie) { + ws_server_t *ws; + + if ((ws = malloc(sizeof(ws_server_t))) == NULL) { + goto fail; + } + if ((ws->fp = fdopen(fd, "r+")) == NULL) { + goto fail; + } + if (_ws_handshake(ws)) { + goto fail; + } + ws->fd = fd; + ws->func = func; + ws->cookie = cookie; + return ws; +fail: + if (ws != NULL) { + free(ws); + if (ws->fp) { + fclose(ws->fp); + return NULL; + } + } + close(fd); + return NULL; +} + +void ws_close(ws_server_t *ws) { + fclose(ws->fp); + free(ws); +} diff --git a/tools/websocket.h b/tools/websocket.h @@ -0,0 +1,33 @@ +/* websocket.c + * + * Copyright 2015 Brian Swetland <swetland@frotz.net> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _WEBSOCKET_SERVER_H_ +#define _WEBSOCKET_SERVER_H_ + +typedef struct ws_server ws_server_t; + +typedef void (*ws_callback_t)(unsigned op, void *msg, size_t len, void *cookie); + +ws_server_t *ws_handshake(int fd, ws_callback_t func, void *cookie); +int ws_process_messages(ws_server_t *ws); + +int ws_send_text(ws_server_t *ws, const void *msg, size_t len); +int ws_send_binary(ws_server_t *ws, const void *msg, size_t len); + +void ws_close(ws_server_t *ws); + +#endif