cake
/
libp7
Archived
1
0
Fork 1
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libp7/src/packet/recv.c

554 lines
16 KiB
C

/* ************************************************************************** */
/* _____ _ */
/* packet/recv.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libp7 | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/10/13 07:33:17 |___/ */
/* */
/* ************************************************************************** */
#include <libp7/internals.h>
#include <stdlib.h>
#include <string.h>
/* ************************************************************************** */
/* Data layouts */
/* ************************************************************************** */
/**
* packetdata_ackext_t:
* The extended ack packet data field layout.
*/
typedef struct {
/* hardware identifier - ASCII */
unsigned char hwid[8];
/* processor identifier - ASCII */
unsigned char cpuid[16];
/* preprogrammed ROM capacity - ASCII-hex (Ko) */
unsigned char preprog_rom_capacity[8];
/* flash ROM capacity - ASCII-hex (Ko) */
unsigned char flash_rom_capacity[8];
/* RAM capacity - ASCII-hex (Ko) */
unsigned char ram_capacity[8];
/* preprogrammed ROM version - "xx.xx.xx" + type */
unsigned char preprog_rom_version[16];
/* bootcode version - "xx.xx.xx" + type */
unsigned char bootcode_version[16];
/* bootcode offset - ASCII-hex */
unsigned char bootcode_offset[8];
/* bootcode size - ASCII-hex (Ko) */
unsigned char bootcode_size[8];
/* OS version - "xx.xx.xx" + type */
unsigned char os_version[16];
/* OS offset - ASCII-hex */
unsigned char os_offset[8];
/* OS size - ASCII-hex (Ko) */
unsigned char os_size[8];
/* protocol version - "x.xx" */
unsigned char protocol_version[4];
/* product ID */
unsigned char product_id[16];
/* name set by user in SYSTEM */
unsigned char username[16];
} packetdata_ackext_t;
/**
* packetdata_data_t:
* The data packet data field layout.
*/
typedef struct {
unsigned char total_number[4];
unsigned char current_number[4];
unsigned char data[];
} packetdata_data_t;
/* ************************************************************************** */
/* Useful functions */
/* ************************************************************************** */
/**
* p7_recv_getver:
* Get version from 16-bytes char buffer.
*
* data is not a string as it doesn't finish with an '\0'.
*
* @arg ver the version destination
* @arg data the raw version data ("xx.xx.xx" + type)
*/
static void p7_recv_getver(p7_version_t *ver, unsigned char data[16])
{
char *t;
/* get major */
ver->major = p7_getascii(data, 2);
/* get minor */
ver->minor = p7_getascii(&data[3], 2);
/* get rev */
ver->rev = p7_getascii(&data[6], 2);
/* get type */
memcpy(&ver->type, &data[8], 8);
ver->type[8] = '\0';
if ((t = strchr(ver->type, 0xFF))) *t = '\0';
/* log */
log_info("major: 0x%02x, minor: 0x%02x, rev: 0x%02x (type: '%s')",
ver->major, ver->minor, ver->rev, ver->type);
}
/**
* p7_recv_getack:
* Get data from ack data field.
*
* Layout is described in the fxReverse projet documentation.
*
* @arg ack the ack packet destination
* @arg data the raw data
*/
static void p7_recv_getack(p7_packetack_t *ack, void *data)
{
packetdata_ackext_t *d = data;
char *t; /* temporary char pointer */
/* log */
log_info("ack packet is extended");
/* hardware identifier */
memcpy(ack->hwid, d->hwid, 8); ack->hwid[8] = '\0';
log_info("hardware identifier is '%s'", ack->hwid);
/* processor identifier */
memcpy(ack->cpuid, d->cpuid, 16); ack->cpuid[16] = '\0';
log_info("cpu identifier is '%s'", ack->cpuid);
/* preprogrammed ROM capacity */
ack->preprog_rom_capacity = p7_getascii(d->preprog_rom_capacity, 8) * 1000;
log_info("preprogrammed ROM capacity is %luo",
ack->preprog_rom_capacity);
/* preprogrammed ROM version */
log_info("preprogrammed ROM version is the following :");
p7_recv_getver(&ack->preprog_rom_version, d->preprog_rom_version);
/* preprogrammed ROM looks wiped out */
ack->preprog_rom_wiped = (ack->preprog_rom_capacity == 0);
if (ack->preprog_rom_wiped) {
log_warn("preprogrammed ROM info looks wiped out !");
}
/* flash ROM capacity */
ack->flash_rom_capacity = p7_getascii(d->flash_rom_capacity, 8) * 1000;
log_info("flash ROM capacity is %luo",
ack->flash_rom_capacity);
/* RAM capacity */
ack->ram_capacity = p7_getascii(d->ram_capacity, 8);
log_info("RAM capacity is %luo", ack->ram_capacity);
/* bootcode looks wiped out */
ack->bootcode_wiped = (d->bootcode_version[2] == 0xFF);
if (ack->bootcode_wiped) { log_warn("bootcode info looks wiped out !"); }
/* bootcode version */
log_info("bootcode version is the following :");
p7_recv_getver(&ack->bootcode_version, d->bootcode_version);
/* bootcode offset */
ack->bootcode_offset = p7_getascii(d->bootcode_offset, 8);
log_info("bootcode offset is %lu", ack->bootcode_offset);
/* bootcode size */
ack->bootcode_size = p7_getascii(d->bootcode_size, 8);
log_info("bootcode size is %lu", ack->bootcode_size);
/* OS version */
log_info("OS version is the following :");
p7_recv_getver(&ack->os_version, d->os_version);
/* OS offset */
ack->os_offset = p7_getascii(d->os_offset, 8);
log_info("OS offset is 0x%08lx", ack->os_offset);
/* OS size */
ack->os_size = p7_getascii(d->os_size, 8);
log_info("OS size is %luo", ack->os_size);
/* protocol version */
ack->protocol_version.major = p7_getascii(d->protocol_version, 1);
ack->protocol_version.minor = p7_getascii(&d->protocol_version[2], 2);
log_info("protocol version is %d.%02d",
ack->protocol_version.major, ack->protocol_version.minor);
/* product ID */
memcpy(ack->product_id, d->product_id, 16); ack->product_id[16] = '\0';
if ((t = strchr(ack->product_id, 0xFF))) *t = '\0';
log_info("product ID is %s", ack->product_id);
/* username */
memcpy(ack->username, d->username, 16); ack->username[16] = '\0';
if ((t = strchr(ack->username, 0xFF))) *t = '\0';
log_info("username is %s", ack->username);
}
/**
* p7_recv_getcmd:
* Get data from command data field.
*
* Layout is described in the fxReverse projet documentation.
*
* @arg cmd the command packet destination
* @arg raw the raw data
* @arg raw_size the raw data size
* @return if it was correct
*/
static int p7_recv_getcmd(p7_packetcommand_t *cmd, const unsigned char *raw,
size_t raw_size)
{
/* check up_and_run command */
if (cmd->code == p7_cc_osu_upandrun) {
if (raw_size != 12) return (0);
cmd->uploadsize = p7_getascii(raw, 4);
cmd->loadaddr = p7_getascii(&raw[4], 4);
cmd->straddr = p7_getascii(&raw[8], 4);
return (1);
}
/* check minimum size */
if (raw_size < 24) return (0);
/* get overwrite */
cmd->overwrite = p7_getascii(raw, 2);
log_info("overwrite mode is '%s' (0x%02x)",
p7_getowstring(cmd->overwrite), cmd->overwrite);
/* get data type (?) */
cmd->datatype = p7_getascii(&raw[2], 2);
log_info("datatype is *todo: put MCS datatypes* (0x%02x)", cmd->datatype);
/* filesize */
cmd->filesize = p7_getascii(&raw[4], 8);
log_info("filesize is %luo", cmd->filesize);
/* args length */
size_t l1 = p7_getascii(&raw[12], 2);
size_t l2 = p7_getascii(&raw[14], 2);
size_t l3 = p7_getascii(&raw[16], 2);
size_t l4 = p7_getascii(&raw[18], 2);
size_t l5 = p7_getascii(&raw[20], 2);
size_t l6 = p7_getascii(&raw[22], 2);
/* re-check size */
if (raw_size != 24 + l1 + l2 + l3 + l4 + l5 + l6) return (0);
/* args */
const unsigned char *p = &raw[24];
memcpy(cmd->args[0], p, l1); cmd->args[0][l1] = '\0'; p += l1;
memcpy(cmd->args[1], p, l2); cmd->args[1][l2] = '\0'; p += l2;
memcpy(cmd->args[2], p, l3); cmd->args[2][l3] = '\0'; p += l3;
memcpy(cmd->args[3], p, l4); cmd->args[3][l4] = '\0'; p += l4;
memcpy(cmd->args[4], p, l5); cmd->args[4][l5] = '\0'; p += l5;
memcpy(cmd->args[5], p, l6); cmd->args[5][l6] = '\0';
/* log args */
if (l1) { log_info("D1 is '%s'", cmd->args[0]); }
if (l2) { log_info("D2 is '%s'", cmd->args[1]); }
if (l3) { log_info("D3 is '%s'", cmd->args[2]); }
if (l4) { log_info("D4 is '%s'", cmd->args[3]); }
if (l5) { log_info("D5 is '%s'", cmd->args[4]); }
if (l6) { log_info("D6 is '%s'", cmd->args[5]); }
/* it worked */
return (1);
}
/**
* p7_recv_getdata:
* Get data from data packet data field.
*
* @arg data organized data with fields
* @arg raw raw data
* @arg raw_size raw data size
*/
static void p7_recv_getdata(p7_packetdata_t *data, void *raw,
p7ushort_t raw_size)
{
packetdata_data_t *d = raw;
/* total number */
data->total = p7_getascii(d->total_number, 4);
log_info("Total data packets : %lu", data->total);
/* current id */
data->id = p7_getascii(d->current_number, 4);
log_info("Data packet ID : %lu", data->id);
/* data */
data->size = raw_size - 8;
memcpy(data->data, d->data, raw_size - 8);
log_info("Decoded data (%luo) :", data->size);
logm_info(data->data, data->size);
}
/* ************************************************************************** */
/* Main functions */
/* ************************************************************************** */
/**
* complete_packet:
* Complete the packet with N bytes.
*
* Uses stream buffering to achieve this.
*
* @arg stream the P7 stream.
* @arg buf the buffer.
* @arg recv pointer to currently received bytes.
* @arg with complete with this number of byte.
* @return the error (0 if ok)
*/
static int complete_packet(p7_stream_t *stream,
unsigned char *buf, size_t *recv, size_t with)
{
int err = p7_read(stream, &buf[*recv], with);
*recv += with;
return (err);
}
/**
* p7_recv_main:
* Receive a packet.
*
* This is the main receiving function. It receives, parses, and returns
* if everything has been successfully done or not.
* It uses buffered I/O to read progressively all of the packet.
*
* @arg handle the libp7 handler
* @return the error (0 if ok)
*/
#define buffer handle->_recv_buffer
#define COMPLETE_PACKET(N) { \
int COMP_PACKET_err; \
if ((COMP_PACKET_err = complete_packet(handle->_stream, \
buffer, &received, (N)))) \
return (COMP_PACKET_err); \
}
static int p7_recv_main(p7_handle_t *handle)
{
/* prepare reception */
size_t received = 0;
/* log packet */
log_info("receiving packet...");
/* check type */
COMPLETE_PACKET(1)
response.type = buffer[0];
/* image has a particular format starting from here, look for it now! */
if (response.type == p7_pt_ohp) {
COMPLETE_PACKET(5)
/* get image by its type */
unsigned int image_size = 0;
if (!memcmp(&buffer[1], "TYP01", 5)) {
log_info("This is normal VRAM, we know it !");
response.screen.type = p7_pscr_typ01;
image_size = 1024;
} else {
log_error("Unknown image type : %.5s", &buffer[1]);
return (p7_error_checksum);
}
/* complete packet */
log_info("Get screen and checksum (%uo)", image_size + 2);
COMPLETE_PACKET(image_size + 2)
memcpy(&response.screen.vram, &buffer[6], image_size);
/* log */
unsigned int packet_size = 6 + image_size + 2;
log_info("received the following [screen] packet (%uo) :", packet_size);
logm_info(buffer, packet_size);
/* check the sum */
if (!image_size
|| p7_checksum(buffer, packet_size)
!= p7_getascii(&buffer[packet_size - 2], 2))
return (p7_error_checksum);
/* and return the packet */
return (0);
}
/* subtype and extended */
COMPLETE_PACKET(3)
p7ushort_t subtype = p7_getascii(&buffer[1], 2);
/* - extended (beginning) - */
p7ushort_t data_size = 0;
int is_extended = (buffer[3] == '1');
if (is_extended) {
/* get data size */
COMPLETE_PACKET(4)
data_size = p7_getascii(&buffer[4], 4);
/* get data */
COMPLETE_PACKET(data_size + 2)
} else
COMPLETE_PACKET(2)
/* check if we should read a binary zero */
int is_binary_zero = (response.type == p7_pt_cmd
&& subtype == p7_cc_osu_upandrun);
if (is_binary_zero) COMPLETE_PACKET(1)
/* log */
p7ushort_t packet_size = 6 + 4 * !!is_extended + data_size;
log_info("received the following [normal] packet (%luo) :", packet_size);
logm_info(buffer, packet_size);
/* calculate checksum */
unsigned int calculated_checksum = p7_checksum(buffer, packet_size);
unsigned int found_checksum = p7_getascii(&buffer[packet_size - 2], 2);
if (calculated_checksum != found_checksum)
return (p7_error_checksum);
/* - extended (finish) - */
if (is_extended)
data_size = p7_decode(&buffer[8], &buffer[8], data_size);
/* get fields out for specific packets */
switch (response.type) {
/* - for command - */
case p7_pt_cmd:
log_info("packet was interpreted as a command one");
response.command.code = subtype;
log_info("command is '%s' (0x%02lx)", p7_getcmdstring(subtype), subtype);
if (is_extended
&& !p7_recv_getcmd(&response.command, &buffer[8], data_size))
return (p7_error_checksum);
break;
/* - for data - */
case p7_pt_data:
log_info("packet was interpreted as a data one");
p7_recv_getdata(&response.data, &buffer[8], data_size);
break;
/* - for roleswap - */
case p7_pt_roleswp:
log_info("packet was interpreted as a roleswap one");
log_info("becoming active again");
handle->_active = 1;
break;
/* - for check - */
case p7_pt_check:
log_info("packet was interpreted as a check one");
response.check.initial = !subtype;
break;
/* - for ack - */
case p7_pt_ack:
log_info("packet was interpreted as an ack one");
response.ack.overwrite = (subtype == 0x01);
response.ack.extended = (subtype == 0x02);
if (response.ack.extended) p7_recv_getack(&response.ack, &buffer[8]);
break;
/* - for error - */
case p7_pt_error:
log_info("packet was interpreted as an error one");
log_info("error is '%s' (0x%02lx)",
p7_geterrstring(subtype), subtype);
response.error.code = subtype;
break;
/* - for termination - */
case p7_pt_terminate:
log_info("packet was interpreted as a terminate one");
log_info("termination cause is '%s' (0x%02lx)",
p7_gettermstring(subtype), subtype);
response.term.reason = subtype;
break;
/* other */
default:
log_fatal("bad type received ! type was 0x%02x", response.type);
return (p7_error_unknown);
}
/* finally, return the packet */
return (0);
}
/**
* p7_recv:
* Receives packet, checks for errors.
*
* @arg handle the libp7 handler
* @arg checksum if 0 and invalid checksum, ignore and receive again
* @return if it worked
*/
int p7_recv(p7_handle_t *handle, int checksum)
{
/* check if handler is initialized */
if (!handle)
return (p7_error_uninitialized);
/* main receiving loop */
int tries = 2, err = 0;
int wasresend = 0;
do {
/* get packet */
err = p7_recv_main(handle);
/* if there was a fail */
if (err) switch (err) {
/* if calc couldn't be found, terminate communication */
case p7_error_nocalc:
tries = 0;
break;
/* if it is a timeout, remove a try */
case p7_error_timeout:
tries--;
/* send a check */
err = p7_send_check(handle);
if (err) return (err);
/* set things */
wasresend = 0;
err = p7_error_timeout;
break;
/* if it is a bad checksum, ask for resend ; also,
* reset tries number in case of bad checksum between timeouts */
case p7_error_checksum:
/* if we shouldn't bother sending resend error, just
* transmit checksum error */
if (!checksum) break;
/* if we receive an invalid checksum while we are in packet
* shifting mode, or if we received an error when we sent a
* resend error, cut connexion... we can't do anything. */
if (handle->_shifted || wasresend)
return (p7_error_irrecoverable);
/* otherwise, send resend error */
err = p7_send_err_resend(handle);
if (err) return (err);
/* set things */
wasresend = 1;
err = p7_error_checksum;
break;
/* should not catch bad type & others */
default:
return (p7_error_unknown);
}
} while (err && tries);
/* return the error */
return (err);
}