2024-10-01 22:48:28 +00:00
|
|
|
#include <client.h>
|
|
|
|
#include <stdio.h>
|
2024-10-02 01:24:48 +00:00
|
|
|
#include <stdint.h>
|
2024-10-01 22:48:28 +00:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2024-10-02 01:24:48 +00:00
|
|
|
bool rdb_client_write_i8_hex(RdbClient *self, uint8_t value) {
|
|
|
|
char hi, lo;
|
|
|
|
return char_to_hex_digits(value, &hi, &lo)
|
|
|
|
&& write_char(self, hi)
|
|
|
|
&& write_char(self, lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool rdb_client_write_i32_hex(RdbClient *self, uint32_t value) {
|
|
|
|
return rdb_client_write_i8_hex(self, (uint8_t) value)
|
|
|
|
&& rdb_client_write_i8_hex(self, (uint8_t) (value >> 8))
|
|
|
|
&& rdb_client_write_i8_hex(self, (uint8_t) (value >> 16))
|
|
|
|
&& rdb_client_write_i8_hex(self, (uint8_t) (value >> 24));
|
|
|
|
}
|
|
|
|
|
2024-10-01 22:48:28 +00:00
|
|
|
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;
|
|
|
|
}
|