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