1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00
putty-source/sshcommon.c

289 lines
7.1 KiB
C
Raw Normal View History

/*
* Supporting routines used in common by all the various components of
* the SSH system.
*/
#include <assert.h>
#include "ssh.h"
#include "sshchan.h"
/* ----------------------------------------------------------------------
* Implementation of PacketQueue.
*/
void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
{
assert(!node->next);
assert(!node->prev);
node->next = &pqb->end;
node->prev = pqb->end.prev;
node->next->prev = node;
node->prev->next = node;
}
void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
{
assert(!node->next);
assert(!node->prev);
node->prev = &pqb->end;
node->next = pqb->end.next;
node->next->prev = node;
node->prev->next = node;
}
static PktIn *pq_in_get(PacketQueueBase *pqb, int pop)
{
PacketQueueNode *node = pqb->end.next;
if (node == &pqb->end)
return NULL;
if (pop) {
node->next->prev = node->prev;
node->prev->next = node->next;
node->prev = node->next = NULL;
}
return FROMFIELD(node, PktIn, qnode);
}
static PktOut *pq_out_get(PacketQueueBase *pqb, int pop)
{
PacketQueueNode *node = pqb->end.next;
if (node == &pqb->end)
return NULL;
if (pop) {
node->next->prev = node->prev;
node->prev->next = node->next;
node->prev = node->next = NULL;
}
return FROMFIELD(node, PktOut, qnode);
}
void pq_in_init(PktInQueue *pq)
{
pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
pq->get = pq_in_get;
}
void pq_out_init(PktOutQueue *pq)
{
pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
pq->get = pq_out_get;
}
void pq_in_clear(PktInQueue *pq)
{
PktIn *pkt;
while ((pkt = pq_pop(pq)) != NULL)
ssh_unref_packet(pkt);
}
void pq_out_clear(PktOutQueue *pq)
{
PktOut *pkt;
while ((pkt = pq_pop(pq)) != NULL)
ssh_free_pktout(pkt);
}
/*
* Concatenate the contents of the two queues q1 and q2, and leave the
* result in qdest. qdest must be either empty, or one of the input
* queues.
*/
void pq_base_concatenate(PacketQueueBase *qdest,
PacketQueueBase *q1, PacketQueueBase *q2)
{
struct PacketQueueNode *head1, *tail1, *head2, *tail2;
/*
* Extract the contents from both input queues, and empty them.
*/
head1 = (q1->end.next == &q1->end ? NULL : q1->end.next);
tail1 = (q1->end.prev == &q1->end ? NULL : q1->end.prev);
head2 = (q2->end.next == &q2->end ? NULL : q2->end.next);
tail2 = (q2->end.prev == &q2->end ? NULL : q2->end.prev);
q1->end.next = q1->end.prev = &q1->end;
q2->end.next = q2->end.prev = &q2->end;
/*
* Link the two lists together, handling the case where one or
* both is empty.
*/
if (tail1)
tail1->next = head2;
else
head1 = head2;
if (head2)
head2->prev = tail1;
else
tail2 = head1;
/*
* Check the destination queue is currently empty. (If it was one
* of the input queues, then it will be, because we emptied both
* of those just a moment ago.)
*/
assert(qdest->end.next == &qdest->end);
assert(qdest->end.prev == &qdest->end);
/*
* If our concatenated list has anything in it, then put it in
* dest.
*/
if (!head1) {
assert(!tail2);
} else {
assert(tail2);
qdest->end.next = head1;
qdest->end.prev = tail2;
head1->prev = &qdest->end;
tail2->next = &qdest->end;
}
}
/* ----------------------------------------------------------------------
* Low-level functions for the packet structures themselves.
*/
static void ssh_pkt_BinarySink_write(BinarySink *bs,
const void *data, size_t len);
PktOut *ssh_new_packet(void)
{
PktOut *pkt = snew(PktOut);
BinarySink_INIT(pkt, ssh_pkt_BinarySink_write);
pkt->data = NULL;
pkt->length = 0;
pkt->maxlen = 0;
pkt->downstream_id = 0;
pkt->additional_log_text = NULL;
pkt->qnode.next = pkt->qnode.prev = NULL;
return pkt;
}
static void ssh_pkt_ensure(PktOut *pkt, int length)
{
if (pkt->maxlen < length) {
pkt->maxlen = length + 256;
pkt->data = sresize(pkt->data, pkt->maxlen, unsigned char);
}
}
static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
{
pkt->length += len;
ssh_pkt_ensure(pkt, pkt->length);
memcpy(pkt->data + pkt->length - len, data, len);
}
static void ssh_pkt_BinarySink_write(BinarySink *bs,
const void *data, size_t len)
{
PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut);
ssh_pkt_adddata(pkt, data, len);
}
void ssh_unref_packet(PktIn *pkt)
{
if (--pkt->refcount <= 0)
sfree(pkt);
}
void ssh_free_pktout(PktOut *pkt)
{
sfree(pkt->data);
sfree(pkt);
}
/* ----------------------------------------------------------------------
* Implement zombiechan_new() and its trivial vtable.
*/
static void zombiechan_free(Channel *chan);
static int zombiechan_send(Channel *chan, int is_stderr, const void *, int);
static void zombiechan_set_input_wanted(Channel *chan, int wanted);
static void zombiechan_do_nothing(Channel *chan);
static void zombiechan_open_failure(Channel *chan, const char *);
static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof);
static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
static const struct ChannelVtable zombiechan_channelvt = {
zombiechan_free,
zombiechan_do_nothing, /* open_confirmation */
zombiechan_open_failure,
zombiechan_send,
zombiechan_do_nothing, /* send_eof */
zombiechan_set_input_wanted,
zombiechan_log_close_msg,
zombiechan_want_close,
};
Channel *zombiechan_new(void)
{
Channel *chan = snew(Channel);
chan->vt = &zombiechan_channelvt;
chan->initial_fixed_window_size = 0;
return chan;
}
static void zombiechan_free(Channel *chan)
{
assert(chan->vt == &zombiechan_channelvt);
sfree(chan);
}
static void zombiechan_do_nothing(Channel *chan)
{
assert(chan->vt == &zombiechan_channelvt);
}
static void zombiechan_open_failure(Channel *chan, const char *errtext)
{
assert(chan->vt == &zombiechan_channelvt);
}
static int zombiechan_send(Channel *chan, int is_stderr,
const void *data, int length)
{
assert(chan->vt == &zombiechan_channelvt);
return 0;
}
static void zombiechan_set_input_wanted(Channel *chan, int enable)
{
assert(chan->vt == &zombiechan_channelvt);
}
static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
{
return TRUE;
}
/* ----------------------------------------------------------------------
* Centralised standard methods for other channel implementations to
* borrow.
*/
void chan_remotely_opened_confirmation(Channel *chan)
{
assert(0 && "this channel type should never receive OPEN_CONFIRMATION");
}
void chan_remotely_opened_failure(Channel *chan, const char *errtext)
{
assert(0 && "this channel type should never receive OPEN_FAILURE");
}
int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
{
return FALSE; /* default: never proactively ask for a close */
}