summaryrefslogtreecommitdiff
path: root/reverse-engineering/dosbox_snif/rsp.c
diff options
context:
space:
mode:
Diffstat (limited to 'reverse-engineering/dosbox_snif/rsp.c')
-rw-r--r--reverse-engineering/dosbox_snif/rsp.c243
1 files changed, 243 insertions, 0 deletions
diff --git a/reverse-engineering/dosbox_snif/rsp.c b/reverse-engineering/dosbox_snif/rsp.c
new file mode 100644
index 0000000..c9957b7
--- /dev/null
+++ b/reverse-engineering/dosbox_snif/rsp.c
@@ -0,0 +1,243 @@
+#include "rsp.h"
+
+/* recv() */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <string.h> /* memset(), strncmp() */
+#include <stdio.h> /* perror(), fprintf(), snprintf(), sscanf() */
+#include <stdlib.h> /* malloc(), free() */
+#include <unistd.h> /* close() */
+#include <errno.h> /* EAGAIN... */
+
+void _rsp_sniff_garbage(struct rsp_state *rsp) {
+ int recvbytes, rv;
+
+ // Sniff garbages before sending command
+ recvbytes = recv(rsp->sockfd, rsp->response, rsp->data_maxlen, MSG_DONTWAIT);
+ if ( recvbytes < 0 ) {
+ rv = errno;
+ if ( rv == EAGAIN || rv == EWOULDBLOCK ) {
+ /* No garbage found, that's a good news */
+ } else {
+ LOG_DEBUG("! <-(error %i)\n", rv);
+ }
+ recvbytes=0;
+ }
+ rsp->response[recvbytes] = '\0';
+ if ( recvbytes > 0 ) {
+ LOG_DEBUG("! <-'%s'\n",rsp->response);
+ }
+}
+
+int rsp_recv_full(struct rsp_state *rsp) {
+
+ int rv, error=0, complete=0, full=0, sentbytes;
+ ssize_t bufoldpos=0, bufpos=0;
+ char *bom=NULL, *eom=NULL, *ptr;
+ unsigned char c, cack='+';
+ unsigned int computed_checksum, read_checksum;
+
+ do {
+ rv = recv(rsp->sockfd, rsp->response + bufpos, rsp->data_maxlen - bufpos, 0);
+ if ( rv < 0 ) {
+ switch (errno) {
+ case EAGAIN:
+ continue;
+ default:
+ error=1;
+ continue;
+ }
+ }
+ if ( rv == 0 ) {
+ error=1;
+ continue;
+ }
+
+ bufoldpos = bufpos;
+ bufpos += rv;
+
+ if ( bom == NULL ) {
+ bom = memchr(rsp->response + bufoldpos, '$', bufpos - bufoldpos);
+ }
+
+ if ( bom != NULL && eom == NULL ) {
+ eom = memchr(bom, '#', bufpos - (bom - rsp->response) );
+ }
+
+ complete = bom && eom && (eom+2) <= (rsp->response + bufpos); // 2 char checksum after EOM
+ full = (bufpos >= rsp->data_maxlen);
+
+ } while (!error && !complete && !full);
+
+ if (error) return 1;
+ if (full) return 2;
+
+ rsp->response[bufpos]='\0';
+
+ c=eom[3];
+ eom[3]='\0';
+ LOG_DEBUG(" <- '%s'\n", rsp->response);
+
+ if ( (eom+3) < (rsp->response + bufpos - 1)) {
+ eom[3]=c;
+ LOG_DEBUG("? <- '%s'\n", eom+3);
+ }
+
+
+ rsp->response_len = eom - bom - 1;
+ rsp->response_bom = bom;
+ rsp->response_eom = eom;
+
+ computed_checksum=0;
+ for (ptr = bom+1; ptr < eom; ptr++) {
+ computed_checksum = (computed_checksum + ((unsigned char)*ptr) ) % 256;
+ }
+
+ rv = sscanf(eom + 1, "%02x", &read_checksum);
+ if ( rv != 1 || computed_checksum != read_checksum) return 3;
+
+ rsp->replied = 1;
+
+ if (rsp->noackmode !=1) {
+ sentbytes = send(rsp->sockfd, &cack, 1, 0);
+ if ( sentbytes < 1 ) return 4;
+ LOG_DEBUG("-> '+'\n");
+ }
+
+ return 0;
+}
+
+int rsp_init(int sockfd, int data_maxlen, struct rsp_state *rsp) {
+ memset(rsp,0,sizeof(struct rsp_state));
+ rsp->sockfd = sockfd;
+ rsp->data_maxlen = data_maxlen;
+
+ rsp->command = malloc(data_maxlen+1);
+ if ( rsp->command == NULL ) return 1;
+
+ rsp->response = malloc(data_maxlen+1);
+ if ( rsp->response == NULL ) return 1;
+
+ rsp->decoded = malloc(data_maxlen+1);
+ if ( rsp->decoded == NULL ) return 1;
+
+/*
+ rsp_query(rsp, "QStartNoAckMode");
+ if ( rsp_check_and_clear(rsp, "OK") == 0 ) rsp->noackmode=1;
+*/
+ return 0;
+}
+
+void rsp_quit(struct rsp_state *rsp) {
+ //rsp_query(rsp, "k");
+ if ( rsp->command != NULL ) free(rsp->command);
+ if ( rsp->response != NULL ) free(rsp->response);
+ if ( rsp->decoded != NULL ) free(rsp->decoded);
+ close(rsp->sockfd);
+ memset(rsp,0,sizeof(struct rsp_state));
+}
+
+void rsp_send_break(struct rsp_state *rsp) {
+ int sentbytes;
+ char cbreak=3;
+
+ rsp->replied = 0;
+ rsp->response_len = 0;
+
+ _rsp_sniff_garbage(rsp);
+
+ sentbytes = send(rsp->sockfd, &cbreak, 1, 0);
+ if ( sentbytes < 1 ) return;
+ LOG_DEBUG("-> '^C'\n");
+
+ rsp_recv_full(rsp);
+}
+
+void rsp_query(struct rsp_state *rsp, char command[]) {
+ int sentbytes;
+ unsigned int checksum, i;
+ unsigned char c;
+
+
+ rsp->replied = 0;
+ rsp->response_len = 0;
+
+ i=0; checksum=0;
+ while ( (c=command[i++]) != '\0' ) {
+ checksum = (checksum + c ) % 256;
+ }
+
+ rsp->command_len = snprintf(rsp->command, rsp->data_maxlen, "$%s#%02x", command, checksum);
+ if (rsp->command_len < 5) return;
+
+ _rsp_sniff_garbage(rsp);
+
+ sentbytes = send(rsp->sockfd, rsp->command, rsp->command_len, 0);
+ if ( sentbytes < rsp->command_len ) return;
+ LOG_DEBUG("-> '%s'\n", rsp->command);
+
+
+ rsp_recv_full(rsp);
+}
+
+int rsp_check_and_clear(struct rsp_state *rsp, char expected_response[]) {
+
+ int expected_responselen = strlen(expected_response);
+
+ if ( rsp->replied != 1 ) return 1;
+ rsp->replied = 0;
+
+ if ( rsp->response_len < expected_responselen ) return 2;
+ if ( strncmp(rsp->response_bom + 1, expected_response, expected_responselen) != 0 ) return 3;
+
+ return 0;
+}
+
+int rsp_decode(struct rsp_state *rsp) {
+/*
+Response data can be run-length encoded to save space. Run-length encoding replaces runs of identical characters with one instance of the repeated character, followed by a ‘*’ and a repeat count. The repeat count is itself sent encoded, to avoid binary characters in data: a value of n is sent as n+29. For a repeat count greater or equal to 3, this produces a printable ascii character, e.g. a space (ascii code 32) for a repeat count of 3. (This is because run-length encoding starts to win for counts 3 or more.) Thus, for example, ‘0* ’ is a run-length encoding of “0000”: the space character after ‘*’ means repeat the leading 0 32 - 29 = 3 more times.
+
+The printable characters ‘#’ and ‘$’ or with a numeric value greater than 126 must not be used. Runs of six repeats (‘#’) or seven repeats (‘$’) can be expanded using a repeat count of only five (‘"’). For example, ‘00000000’ can be encoded as ‘0*"00’.
+*/
+ char src, *srcptr, *dstptr;
+ int repe, max;
+
+ if ( rsp->replied != 1 ) return -1;
+
+ dstptr = rsp->decoded;
+ for ( srcptr = rsp->response_bom+1; srcptr < rsp->response_eom; srcptr++ ) {
+ // FIXME : implemnt RLE decoding
+ src = *srcptr;
+ switch (src) {
+ case '*':
+ srcptr++;
+ repe=(*srcptr)-29;
+ if (repe < 2 ) return -1;
+ max = rsp->decoded + rsp->data_maxlen - 1 - dstptr;
+ if ( repe > max ) { repe = max; }
+ memset(dstptr, *(srcptr-2), repe);
+ dstptr += repe;
+ break;
+ default:
+ *(dstptr++) = *srcptr;
+ break;
+ }
+ }
+ *dstptr='\0';
+
+ return dstptr - rsp->decoded;
+}
+
+int rsp_poke(struct rsp_state *rsp, unsigned int poke_addr, unsigned int poke_value) {
+ char command[16];
+ printf("DEBUG : rsp_poke()\n");
+ if ( snprintf(command, 15, "M %4x,2:%2x", poke_addr & 0xffff, poke_value & 0xff) < 11 ) return 1;
+ printf("DEBUG : command == '%s'\n", command);
+ rsp_query(rsp, command); // Send the poke command to GDB stub
+ printf("DEBUG : response == '%s'\n", rsp->response);
+ if ( rsp_check_and_clear(rsp, "OK") != 0 ) return 2;
+
+ return 0;
+}
+