/*
* Copyright © 2013 Benjamin Franzke
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include "econpacket.h"
#include "econproto.h"
static void
init_iov(struct econ_packet *pkt)
{
pkt->iov[0].iov_base = &pkt->hdr;
pkt->iov[0].iov_len = sizeof pkt->hdr;
pkt->iov[1].iov_base = &pkt->cmd;
pkt->iov[1].iov_len = sizeof pkt->cmd;
pkt->iov[2].iov_base = &pkt->rec;
pkt->iov[2].iov_len = sizeof pkt->rec;
/* To receive oversized commands */
pkt->iov[3].iov_base = pkt->long_data;
pkt->iov[3].iov_len = sizeof pkt->long_data;
}
static void
init_header(struct econ_header *ehdr, int cmd)
{
memset(ehdr, 0, sizeof *ehdr);
strncpy(ehdr->magicnum, ECON_MAGIC_NUMBER, ECON_MAGICNUM_SIZE);
strncpy(ehdr->version, ECON_PROTO_VERSION, ECON_PROTOVER_MAXLEN);
ehdr->datasize = 0;
ehdr->commandID = cmd;
}
void
epkt_init(struct econ_packet *pkt, int cmd)
{
init_iov(pkt);
memset(&pkt->rec, 0, sizeof pkt->rec);
memset(&pkt->cmd, 0, sizeof pkt->cmd);
init_header(&pkt->hdr, cmd);
/* For irregular commands like 21 */
pkt->long_data_size = 0;
}
int
epkt_send(int fd, struct econ_packet *pkt)
{
int i = 1;
if (pkt->hdr.datasize > 0) {
i++;
if (pkt->cmd.recordCount == 1)
i++;
}
return writev(fd, pkt->iov, i);
}
int
epkt_read(int fd, struct econ_packet *pkt)
{
union { ssize_t s; size_t u; } len;
init_iov(pkt);
pkt->long_data_size = 0;
len.s = readv(fd, pkt->iov, 4);
if (len.s < 0)
return -1;
if (len.u < sizeof(struct econ_header)) {
fprintf(stderr, "epkt_read: error: incomplete header\n");
return -1;
}
if (pkt->hdr.datasize == 0) {
if (len.u > sizeof(struct econ_header))
return -1;
return 0;
}
fprintf(stderr, "epkt_read: len.u: %zd, cmd: %d, datasize: %d\n",
len.u, pkt->hdr.commandID, pkt->hdr.datasize);
if (len.u != sizeof(struct econ_header) + pkt->hdr.datasize) {
fprintf(stderr, "packet has invalid datasize\n");
return -1;
}
if (pkt->hdr.datasize < sizeof(struct econ_command)) {
fprintf(stderr,
"epkt_read: error: command to short\n");
return -1;
}
/* Keepalive is an irregular command if datasize > 0:
* the regular field recordCount is 1,
* without actually having one. */
if (pkt->hdr.commandID == E_CMD_KEEPALIVE)
return 0;
/* Handle irregular long datasize */
if (pkt->hdr.datasize > (sizeof(struct econ_command) +
sizeof(struct econ_record))) {
switch (pkt->hdr.commandID) {
case E_CMD_21:
break;
default:
fprintf(stderr, "epkt_read: received cmd: %d"
" with oversized datasize\n",
pkt->hdr.commandID);
exit(EXIT_FAILURE);
}
pkt->long_data_size = (pkt->hdr.datasize -
(sizeof (struct econ_command) +
sizeof (struct econ_record)));
return 0;
}
if (pkt->cmd.recordCount > 0) {
if (pkt->cmd.recordCount > 1) {
fprintf(stderr, "epkt_read: did not expect a packet "
"with more than one record: %d, datasize: %d.\n",
pkt->cmd.recordCount, pkt->hdr.datasize);
return -1;
}
if (pkt->hdr.datasize != (sizeof (struct econ_command) +
sizeof (struct econ_record))) {
fprintf(stderr, "epkt_read: datasize incorrect, cmd: %d\n",
pkt->hdr.commandID);
return -1;
}
}
return 0;
}