gdb-server/client.c

214 lines
5.0 KiB
C
Raw Normal View History

2024-10-01 22:48:28 +00:00
#include <client.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
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;
}