/* * Supporting routines used in common by all the various components of * the SSH system. */ #include #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 */ }