kdp-transactions.c [plain text]
#include "kdp-transactions.h"
#include "kdp-protocol.h"
#include <stdlib.h>
#include "defs.h"
#define assert CHECK_FATAL
static kdp_return_t
kdp_exception_reply (kdp_connection *c, kdp_pkt_t * response)
{
kdp_return_t kdpret;
kdp_exception_ack_t ack;
CHECK_FATAL (kdp_is_connected (c));
CHECK_FATAL (kdp_is_bound (c));
ack.hdr.request = KDP_EXCEPTION;
ack.hdr.is_reply = 1;
ack.hdr.seq = response->exception.hdr.seq;
ack.hdr.key = response->exception.hdr.key;
kdpret = kdp_transmit_exception (c, (kdp_pkt_t *) & ack);
if (kdpret != RR_SUCCESS)
{
c->logger (KDP_LOG_ERROR,
"kdp_exception_reply: unable to acknowledge exception: %s\n",
kdp_return_string (kdpret));
}
return kdpret;
}
kdp_return_t
kdp_exception_wait (kdp_connection *c, kdp_pkt_t * response, int timeout)
{
if (c->saved_exception_pending)
{
memcpy (response, c->saved_exception, KDP_MAX_PACKET_SIZE);
c->saved_exception_pending = 0;
c->logger (KDP_LOG_DEBUG, "kdp_exception_wait: "
"returning previously saved exception (sequence number is %d)",
response->hdr.seq);
return RR_SUCCESS;
}
for (;;)
{
kdp_return_t kdpret;
kdpret = kdp_receive_exception (c, response, timeout);
if (kdpret != RR_SUCCESS)
{
return kdpret;
}
kdpret = kdp_exception_reply (c, response);
if (kdpret != RR_SUCCESS)
{
return kdpret;
}
if (response->hdr.seq == c->exc_seqno)
{
c->exc_seqno = (c->exc_seqno + 1) % 256;
c->logger (KDP_LOG_DEBUG, "kdp_exception_wait: "
"returning previously saved exception (sequence number is %d)",
response->hdr.seq);
break;
}
else if (((response->hdr.seq + 1) % 256) == c->exc_seqno)
{
c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: "
"ignoring duplicate of previous exception (sequence number was %d)\n",
response->hdr.seq);
continue;
}
else
{
c->logger (KDP_LOG_ERROR, "kdp_exception_wait: "
"unexpected sequence number for exception (expected %d, got %d)\n",
c->exc_seqno, response->hdr.seq);
continue;
}
}
return RR_SUCCESS;
}
kdp_return_t
kdp_reply_wait (kdp_connection *c, kdp_pkt_t * response, int timeout)
{
for (;;)
{
kdp_return_t kdpret;
kdpret = kdp_receive (c, response, timeout);
if (kdpret != RR_SUCCESS)
{
c->logger (KDP_LOG_ERROR,
"kdp_reply_wait: error from kdp_receive: %s\n",
kdp_return_string (kdpret));
return kdpret;
}
if (response->hdr.request == KDP_EXCEPTION)
{
kdpret = kdp_exception_reply (c, response);
if (kdpret != RR_SUCCESS)
{
c->logger (KDP_LOG_ERROR,
"kdp_reply_wait: error from kdp_exception_reply: %s\n",
kdp_return_string (kdpret));
return kdpret;
}
if (response->hdr.seq == c->exc_seqno)
{
c->exc_seqno = (c->exc_seqno + 1) % 256;
if (c->saved_exception_pending)
{
c->logger (KDP_LOG_ERROR, "kdp_reply_wait: "
"unable to save exception for future processing; ignoring\n");
c->logger (KDP_LOG_ERROR, "kdp_reply_wait: "
"exception had sequence number %d\n",
response->hdr.seq);
return RR_IP_ERROR;
}
c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: "
"saving exception for future processing (sequence number is %d)\n",
response->hdr.seq);
memcpy (c->saved_exception, response, KDP_MAX_PACKET_SIZE);
c->saved_exception_pending = 1;
continue;
}
else if (((response->hdr.seq + 1) % 256) == c->exc_seqno)
{
c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: "
"ignoring duplicate of previous exception (sequence number was %d)\n",
response->hdr.seq);
continue;
}
else
{
c->logger (KDP_LOG_ERROR, "kdp_reply_wait: "
"unexpected sequence number for exception (expected %d, got %d)\n",
c->exc_seqno, response->hdr.seq);
continue;
}
}
else
{
if (response->hdr.seq == c->seqno)
{
c->seqno = (c->seqno + 1) % 256;
c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: "
"returning reply (sequence number is %d)\n",
response->hdr.seq);
break;
}
else if (((response->hdr.seq + 1) % 256) == c->exc_seqno)
{
c->logger (KDP_LOG_DEBUG, "kdp_reply_wait: "
"ignoring duplicate of previous reply (sequence number was %d)\n",
response->hdr.seq);
continue;
}
else
{
c->logger (KDP_LOG_ERROR, "kdp_reply_wait: "
"unexpected sequence number for reply (expected %d, got %d)\n",
c->seqno, response->hdr.seq);
continue;
}
}
}
return RR_SUCCESS;
}
kdp_return_t
kdp_transaction (kdp_connection *c,
kdp_pkt_t * request, kdp_pkt_t * response, char *name)
{
kdp_return_t rtn;
int retries = c->retries;
CHECK_FATAL (kdp_is_bound (c));
CHECK_FATAL (request != NULL);
CHECK_FATAL (response != NULL);
CHECK_FATAL (name != NULL);
request->hdr.seq = c->seqno;
request->hdr.key = c->session_key;
request->hdr.is_reply = 0;
rtn = RR_RECV_TIMEOUT;
while (retries--)
{
rtn = kdp_transmit_debug (c, request);
if (rtn != RR_SUCCESS)
{
break;
}
if (c->timed_out)
{
rtn = kdp_reply_wait (c, response, 1);
if (rtn == RR_RECV_TIMEOUT)
{
return rtn;
}
c->logger (KDP_LOG_INFO, "kdp_transaction (%s): "
"host responding; continuing transactions\n", name);
}
else
{
rtn = kdp_reply_wait (c, response, c->receive_timeout);
}
if (rtn == RR_RECV_TIMEOUT)
{
c->logger (KDP_LOG_INFO,
"kdp_transaction (%s): transaction timed out\n", name);
c->logger (KDP_LOG_INFO,
"kdp_transaction (%s): re-sending transaction\n", name);
continue;
}
break;
}
if (rtn == RR_RECV_TIMEOUT)
{
c->logger (KDP_LOG_INFO,
"kdp_transaction (%s): host not responding; will retry\n",
name);
c->timed_out = 1;
return rtn;
}
if (rtn != RR_SUCCESS)
{
return rtn;
}
if ((request->hdr.request != KDP_CONNECT)
&& (response->hdr.key != c->session_key))
{
c->logger (KDP_LOG_ERROR,
"kdp_transaction (%s): invalid session key %d (expected %d)\n",
name, response->hdr.key, c->session_key);
return RR_BAD_ACK;
}
if (!response->hdr.is_reply)
{
c->logger (KDP_LOG_ERROR, "kdp_transaction (%s): "
"response was not tagged as a reply packet\n", name);
return RR_BAD_ACK;
}
if (response->hdr.request != request->hdr.request)
{
c->logger (KDP_LOG_ERROR, "kdp_transaction (%s):"
"packet type of request (%d) does not match packet type of reply (%d)\n",
name, request->hdr.request, response->hdr.request);
return RR_BAD_ACK;
}
return RR_SUCCESS;
}
kdp_return_t
kdp_connect (kdp_connection *c)
{
kdp_return_t ret;
CHECK_FATAL (kdp_is_bound (c));
CHECK_FATAL (!kdp_is_connected (c));
c->request->connect_req.hdr.request = KDP_CONNECT;
c->request->connect_req.req_reply_port = c->reqport;
c->request->connect_req.exc_note_port = c->excport;
strncpy (c->request->connect_req.greeting, "", 64);
c->request->connect_req.greeting[63] = '\0';
ret = kdp_transaction (c, c->request, c->response, "remote_connect");
if (ret != RR_SUCCESS)
{
return ret;
}
if (c->response->writemem_reply.error)
{
c->logger (KDP_LOG_ERROR, "kdp_connect: %s\n",
kdp_return_string (c->response->connect_reply.error));
return RR_CONNECT;
}
c->session_key = c->response->hdr.key;
c->connected = 1;
return RR_SUCCESS;
}
kdp_return_t
kdp_disconnect (kdp_connection *c)
{
kdp_return_t ret;
CHECK_FATAL (kdp_is_connected (c));
c->request->disconnect_req.hdr.request = KDP_DISCONNECT;
ret = kdp_transaction (c, c->request, c->response, "kdp_disconnect");
if (ret != RR_SUCCESS)
{
return ret;
}
c->connected = 0;
return RR_SUCCESS;
}
kdp_return_t
kdp_reattach (kdp_connection *c)
{
kdp_return_t ret;
CHECK_FATAL (!kdp_is_connected (c));
c->request->reattach_req.hdr.request = KDP_REATTACH;
c->request->reattach_req.req_reply_port = c->reqport;
ret = kdp_transaction (c, c->request, c->response, "kdp_reattach");
if (ret != RR_SUCCESS)
{
return ret;
}
c->connected = 1;
return RR_SUCCESS;
}
kdp_return_t
kdp_hostreboot (kdp_connection *c)
{
kdp_return_t ret;
CHECK_FATAL (kdp_is_connected (c));
c->request->hostreboot_req.hdr.request = KDP_HOSTREBOOT;
ret = kdp_transaction (c, c->request, c->response, "kdp_hostreboot");
if (ret != RR_SUCCESS)
{
return ret;
}
c->connected = 0;
return RR_SUCCESS;
}