mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
6a5d4d083a
It's really just a concatenator for a pair of linked lists, but unhelpfully restricted in which of the lists it replaces with the output. Better to have a three-argument function that puts the output wherever you like, whether it overlaps either or neither one of the inputs.
206 lines
4.7 KiB
C
206 lines
4.7 KiB
C
/*
|
|
* 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);
|
|
}
|