From bf969e72b49a05b717eaeb5d156f1a57fc7ed932 Mon Sep 17 00:00:00 2001 From: Simon Gellis Date: Tue, 1 Oct 2024 18:48:28 -0400 Subject: [PATCH] initial version from before --- .gitignore | 1 + client.c | 214 +++++++++++++++++++++++++++++++++++ include/client.h | 25 +++++ main.c | 286 +++++++++++++++++++++++++++++++++++++++++++++++ makefile | 6 + 5 files changed, 532 insertions(+) create mode 100644 .gitignore create mode 100644 client.c create mode 100644 include/client.h create mode 100644 main.c create mode 100644 makefile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c795b05 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build \ No newline at end of file diff --git a/client.c b/client.c new file mode 100644 index 0000000..875d947 --- /dev/null +++ b/client.c @@ -0,0 +1,214 @@ +#include +#include +#include +#include + +static bool write_char(RdbClient *self, char out) { + if (self->len >= RDB_CLIENT_BUFLEN) { + return false; + } + if (out == '#' || out == '$' || out == '}' || out == '*') { + self->full_buf[self->len++] = '}'; + self->chk += '}'; + if (self->len >= RDB_CLIENT_BUFLEN) { + return false; + } + out ^= 0x20; + } + self->full_buf[self->len++] = out; + self->chk += out; + return true; +} + +bool char_to_hex_digit(char in, char *out) { + if (in & 0xf0) { + return false; + } + in &= 0x0f; + if (in > 9) { + *out = 'a' + in - 10; + } else { + *out = '0' + in; + } + return true; +} + +bool hex_digit_to_char(char in, char *out) { + if (in >= '0' && in <= '9') { + *out = in - '0'; + return true; + } + if (in >= 'a' && in <= 'f') { + *out = in - 'a' + 10; + return true; + } + if (in >= 'A' && in <= 'F') { + *out = in - 'A' + 10; + return true; + } + return false; +} + +bool char_to_hex_digits(char in, char *hi, char *lo) { + return char_to_hex_digit((in & 0xf0) >> 4, hi) + && char_to_hex_digit(in & 0x0f, lo); +} + +void rdb_client_init(RdbClient *self, int connfd) { + self->connfd = connfd; + self->len = 0; + self->chk = 0; + self->should_ack = true; +} + +#define BUFFER_LEN 8096 + +typedef struct Buffer { + char buf[BUFFER_LEN]; + size_t len; + size_t index; +} Buffer; + +static Buffer INBUF = { + .len = 0, + .index = 0, +}; + +static ssize_t read_next_char(int connfd, char *in) { + if (INBUF.index >= INBUF.len) { + ssize_t inlen = read(connfd, INBUF.buf, BUFFER_LEN); + if (inlen < 1) { + // either we got an error (-1) or the connection closed (0) + return inlen; + } + INBUF.index = 0; + INBUF.len = inlen; + } + *in = INBUF.buf[INBUF.index++]; + return 1; +} + +ssize_t rdb_client_read(RdbClient *self, char *buf, size_t len) { + // read any acknowledgements and continue + char in; + do { + ssize_t res = read_next_char(self->connfd, &in); + if (res < 1) return res; + } while (in == '+'); + + if (in == '\x03') { + // interrupt from the server + *buf = in; + return 1; + } + + if (in == '-') { + // we don't handle resending right now + return -1; + } + + // now, expect to be at the start of a packet + if (in != '$') { + fprintf(stderr, "unexpected packet start \"%c\"", in); + return -1; + } + + size_t outlen = 0; + char chk = 0; + while (1) { + ssize_t res = read_next_char(self->connfd, &in); + if (res < 1) return res; + + if (in == '#') { + // end of packet, checksum next + break; + } + if (outlen >= len) { + // ran out of room in the buffer + fprintf(stderr, "packet too big for buffer\n"); + return -1; + } + if (in == '}') { + // escape sequence + chk += in; + res = read_next_char(self->connfd, &in); + if (res < 1) return res; + chk += in; + buf[outlen++] = in ^ 0x20; + } else { + chk += in; + buf[outlen++] = in; + } + }; + + // validate the checksum + char hi, lo; + + ssize_t res = read_next_char(self->connfd, &in); + if (res < 1) return res; + if (!hex_digit_to_char(in, &hi)) return -1; + + res = read_next_char(self->connfd, &in); + if (res < 1) return res; + if (!hex_digit_to_char(in, &lo)) return -1; + + char real_chk = (hi << 4) | lo; + if (real_chk != chk) { + fprintf(stderr, "invalid checksum\n"); + return -1; + } + + return outlen; +} + +void rdb_client_begin_packet(RdbClient *self) { + self->len = 0; + self->chk = 0; + if (self->should_ack) { + self->full_buf[self->len++] = '+'; + } + self->full_buf[self->len++] = '$'; +} + +bool rdb_client_write_str(RdbClient *self, const char *str) { + size_t len = strlen(str); + for (size_t i = 0; i < len; ++i) { + if (!write_char(self, str[i])) { + return false; + } + } + return true; +} + +bool rdb_client_write_str_hex(RdbClient *self, const char *str) { + size_t len = strlen(str); + for (size_t i = 0; i < len; ++i) { + char hi, lo; + if (!char_to_hex_digits(str[i], &hi, &lo) + || !write_char(self, hi) + || !write_char(self, lo)) { + return false; + } + } + return true; +} + +int rdb_client_send_packet(RdbClient *self) { + if (self->len + 3 > RDB_CLIENT_BUFLEN) { + return -1; + } + self->full_buf[self->len++] = '#'; + char hi, lo; + if (!char_to_hex_digits(self->chk, &hi, &lo)) { + return -1; + } + self->full_buf[self->len++] = hi; + self->full_buf[self->len++] = lo; + printf("sending command \"%.*s\" %d\n", (int) self->len, self->full_buf, (int)self->len); + + ssize_t rwrite = write(self->connfd, self->full_buf, self->len); + if (rwrite == -1) { + return -1; + } + return 0; +} \ No newline at end of file diff --git a/include/client.h b/include/client.h new file mode 100644 index 0000000..68d71d3 --- /dev/null +++ b/include/client.h @@ -0,0 +1,25 @@ +#ifndef RDBSERVER_CLIENT_H_ +#define RDBSERVER_CLIENT_H_ + +#include +#include +#include + +#define RDB_CLIENT_BUFLEN 8096 + +typedef struct RdbClient { + int connfd; + char full_buf[RDB_CLIENT_BUFLEN]; + size_t len; + char chk; + bool should_ack; +} RdbClient; + +void rdb_client_init(RdbClient *self, int connfd); +ssize_t rdb_client_read(RdbClient *self, char *buf, size_t len); +void rdb_client_begin_packet(RdbClient *self); +bool rdb_client_write_str(RdbClient *self, const char *str); +bool rdb_client_write_str_hex(RdbClient *self, const char *str); +int rdb_client_send_packet(RdbClient *self); + +#endif \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..f13836b --- /dev/null +++ b/main.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include +#include +#include + +const size_t BUFLEN = 8096; + +const char* REGISTERS[] = { + "name:r0;bitsize:32;offset:0;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r1;bitsize:32;offset:4;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:fp;alt-name:r2;bitsize:32;offset:8;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:fp", + "name:sp;alt-name:r3;bitsize:32;offset:12;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:sp", + "name:gp;alt-name:r4;bitsize:32;offset:16;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:tp;alt-name:r5;bitsize:32;offset:20;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r6;bitsize:32;offset:24;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:arg1", + "name:r7;bitsize:32;offset:28;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:arg2", + "name:r8;bitsize:32;offset:32;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:arg3", + "name:r9;bitsize:32;offset:36;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:arg4", + "name:r10;bitsize:32;offset:40;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r11;bitsize:32;offset:44;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r12;bitsize:32;offset:48;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r13;bitsize:32;offset:52;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r14;bitsize:32;offset:56;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r15;bitsize:32;offset:60;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r16;bitsize:32;offset:64;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r17;bitsize:32;offset:68;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r18;bitsize:32;offset:72;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r19;bitsize:32;offset:76;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r20;bitsize:32;offset:80;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r21;bitsize:32;offset:84;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r22;bitsize:32;offset:88;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r23;bitsize:32;offset:92;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r24;bitsize:32;offset:96;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r25;bitsize:32;offset:100;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r26;bitsize:32;offset:104;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r27;bitsize:32;offset:108;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r28;bitsize:32;offset:112;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r29;bitsize:32;offset:116;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:r30;bitsize:32;offset:120;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0", + "name:lp;alt-name:r31;bitsize:32;offset:124;encoding:uint;format:hex;set:General Purpose Registers;dwarf:0;generic:ra", + "name:pc;bitsize:32;offset:128;encoding:uint;format:hex;set:Special Registers;dwarf:0;generic:pc", +}; + +bool read_hex_digit(char digit, char *out) { + if (digit >= '0' && digit <= '9') { + *out = digit - '0'; + return true; + } + if (digit >= 'a' && digit <= 'f') { + *out = digit - 'a' + 10; + return true; + } + if (digit >= 'A' && digit <= 'F') { + *out = digit - 'A' + 10; + return true; + } + return false; +} + +bool read_hex(char *buf, int len, int *out) { + *out = 0; + for (int i = 0; i < len; ++i) { + char outdigit; + if (!read_hex_digit(buf[i], &outdigit)) return false; + *out = (*out << 4) | (int) outdigit; + } + return true; +} + +bool read_hex_byte(char *buf, char *val) { + char digit1, digit2; + if (!read_hex_digit(buf[0], &digit1) || !read_hex_digit(buf[1], &digit2)) { + return false; + } + *val = (digit1 << 4) | digit2; + return true; +} + +int handle_command(RdbClient *client, char *cmd, size_t cmdlen) { + rdb_client_begin_packet(client); + + if (!strncmp(cmd, "\x03", cmdlen)) { + rdb_client_write_str(client, "T05thread:p1.t1;threads:p1.t1;thread-pcs:07000000;00:00000000;01:00000001;20;07000000"); + return rdb_client_send_packet(client); + } + + if (!strncmp(cmd, "QStartNoAckMode", cmdlen)) { + client->should_ack = false; + rdb_client_write_str(client, "OK"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qSupported", 10)) { + rdb_client_write_str(client, "no-resumed+;multiprocess;vContSupported;QNonStop+"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "QThreadSuffixSupported", cmdlen)) { + rdb_client_write_str(client, "OK"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "QListThreadsInStopReply", cmdlen)) { + rdb_client_write_str(client, "OK"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qHostInfo", cmdlen)) { + rdb_client_write_str(client, "triple:"); + rdb_client_write_str_hex(client, "v810-unknown-vb"); + rdb_client_write_str(client, ";endian:little;ptrsize:4;"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qProcessInfo", cmdlen)) { + rdb_client_write_str(client, "pid:1;triple:"); + rdb_client_write_str_hex(client, "v810-unknown-vb"); + rdb_client_write_str(client, "endian:little;ptrsize:4;"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qRegisterInfo", 13)) { + char reg; + if (cmdlen == 14) { + if (!read_hex_digit(cmd[13], ®)) return 1; + } else { + if (!read_hex_byte(cmd + 13, ®)) return 1; + } + if (reg < 33) { + rdb_client_write_str(client, REGISTERS[(size_t) reg]); + } + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qfThreadInfo", cmdlen)) { + rdb_client_write_str(client, "mp1.t1"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qsThreadInfo", cmdlen)) { + rdb_client_write_str(client, "l"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "vCont?", cmdlen)) { + rdb_client_write_str(client, "c;C;s;S"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qVAttachOrWaitSupported", cmdlen)) { + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qC", cmdlen)) { + rdb_client_write_str(client, "QCp1.t1"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "p", 1)) { + rdb_client_write_str(client, "00000000"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "Hc-1", 1)) { + rdb_client_write_str(client, "OK"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "c", cmdlen)) { + printf("running until we hit a breakpoint or the server stops us\n"); + return 0; + } + if (!strncmp(cmd, "?", cmdlen)) { + rdb_client_write_str(client, "T00thread:p1.t1;threads:p1.t1;thread-pcs:00000007;"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "qMemoryRegionInfo:", 18)) { + rdb_client_write_str(client, "start:0;size:100000000;permissions:rx;name:"); + rdb_client_write_str_hex(client, "ROM"); + rdb_client_write_str(client, ";"); + return rdb_client_send_packet(client); + } + if (!strncmp(cmd, "m7000000,100", cmdlen)) { + // hard-coded fake memory region + const char *fake = "80bc000784a0e002a0bc0005a5a00000c0bc0005c6a0000000a81000"; + size_t fakelen = strlen(fake) / 2; + rdb_client_write_str(client, fake); + for (size_t i = fakelen; i < 256; ++i) { + rdb_client_write_str(client, "00"); + } + return rdb_client_send_packet(client); + } + if (*cmd == 'm') { + // all other memory is 0 + int commapos = -1; + for (size_t i = 2; i < cmdlen; ++i) { + if (cmd[i] == ',') { + commapos = (int) i; + break; + } + } + if (commapos == -1) { + fprintf(stderr, "malformed memory read"); + return -1; + } + int address, len; + if (!read_hex(cmd + 1, commapos - 1, &address) || + !read_hex(cmd + commapos + 1, cmdlen - commapos - 1, &len)) { + fprintf(stderr, "malformed memory read"); + return -1; + } + printf("read %d bytes from %d\n", len, address); + for (int i = 0; i < len; ++i) { + rdb_client_write_str(client, "00"); + } + return rdb_client_send_packet(client); + } + fprintf(stderr, "Unrecognized command %.*s\n", (int) cmdlen, cmd); + return rdb_client_send_packet(client); +} + + +int server(int connfd) { + RdbClient client; + rdb_client_init(&client, connfd); + + char buf[BUFLEN]; + while (1) { + ssize_t len = rdb_client_read(&client, buf, BUFLEN); + if (len < 0) { + perror("could not read data"); + return -len; + } else if (len == 0) { + printf("client has disconnected\n"); + return 0; + } else { + printf("received command \"%.*s\"\n", (int) len, buf); + fflush(stdout); + int res = handle_command(&client, buf, len); + if (res != 0) { + return res; + } + // +$QStartNoAckMode#b0 + } + + } + +} + +int main(int argc, char** argv) { + short port; + if (argc > 1) { + char *end; + port = (short) strtol(argv[1], &end, 10); + if (argv[1] == end) { + perror("could not parse port"); + return 1; + } + } else { + port = 8080; + } + + int fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd == -1) { + perror("could not open socket"); + return 1; + } + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { + perror("could not bind socket"); + return 1; + } + if (listen(fd, 1) == -1) { + perror("could not listen on socket"); + return 1; + } + + printf("connecting\n"); + int connfd; + struct sockaddr_in cliaddr; + socklen_t cliaddrlen = sizeof(cliaddr); + connfd = accept(fd, (struct sockaddr *) &cliaddr, &cliaddrlen); + if (connfd == -1) { + perror("could not accept connection"); + return 1; + } + printf("connected\n"); + + int response = server(connfd); + return close(connfd) + || close(fd) + || response; +} \ No newline at end of file diff --git a/makefile b/makefile new file mode 100644 index 0000000..7d2211c --- /dev/null +++ b/makefile @@ -0,0 +1,6 @@ +build: + @mkdir -p build + @gcc main.c client.c -I include -Werror -Wall -Wextra -o ./build/rdb +clean: + @rm -rf build +.PHONY: build clean \ No newline at end of file