diff --git a/ssh.c b/ssh.c index f5cf7142..2749f23f 100644 --- a/ssh.c +++ b/ssh.c @@ -8846,8 +8846,10 @@ static void do_ssh2_connection(void *vctx) * matches any of the table entries we've just modified will go to * the right handler function, and won't come here to confuse us. */ - if (pq_empty_on_to_front_of(&ssh->pq_ssh2_connection, &ssh->pq_full)) + if (pq_peek(&ssh->pq_ssh2_connection)) { + pq_concatenate(&ssh->pq_full, &ssh->pq_ssh2_connection, &ssh->pq_full); queue_idempotent_callback(&ssh->pq_full_consumer); + } /* * Now the connection protocol is properly up and running, with diff --git a/ssh.h b/ssh.h index 7ed45101..f4ef94ef 100644 --- a/ssh.h +++ b/ssh.h @@ -102,7 +102,8 @@ typedef struct PktOutQueue { void pq_base_init(PacketQueueBase *pqb); void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node); void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node); -int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest); +void pq_base_concatenate(PacketQueueBase *dest, + PacketQueueBase *q1, PacketQueueBase *q2); void pq_in_init(PktInQueue *pq); void pq_out_init(PktOutQueue *pq); @@ -117,10 +118,12 @@ void pq_out_clear(PktOutQueue *pq); pq_base_push_front(&(pq)->pqb, &(pkt)->qnode)) #define pq_peek(pq) ((pq)->get(&(pq)->pqb, FALSE)) #define pq_pop(pq) ((pq)->get(&(pq)->pqb, TRUE)) -#define pq_empty_on_to_front_of(src, dst) \ - TYPECHECK((src)->get(&(src)->pqb, FALSE) == \ +#define pq_concatenate(dst, q1, q2) \ + TYPECHECK((q1)->get(&(q1)->pqb, FALSE) == \ + (dst)->get(&(dst)->pqb, FALSE) && \ + (q2)->get(&(q2)->pqb, FALSE) == \ (dst)->get(&(dst)->pqb, FALSE), \ - pq_base_empty_on_to_front_of(&(src)->pqb, &(dst)->pqb)) + pq_base_concatenate(&(dst)->pqb, &(q1)->pqb, &(q2)->pqb)) /* * Packet type contexts, so that ssh2_pkt_type can correctly decode diff --git a/sshcommon.c b/sshcommon.c index 7af086fe..856a8358 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -88,22 +88,66 @@ void pq_out_clear(PktOutQueue *pq) ssh_free_pktout(pkt); } -int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest) +/* + * 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 *srcfirst, *srclast; + struct PacketQueueNode *head1, *tail1, *head2, *tail2; - if (src->end.next == &src->end) - return FALSE; + /* + * Extract the contents from both input queues, and empty them. + */ - srcfirst = src->end.next; - srclast = src->end.prev; - srcfirst->prev = &dest->end; - srclast->next = dest->end.next; - srcfirst->prev->next = srcfirst; - srclast->next->prev = srclast; - src->end.next = src->end.prev = &src->end; + 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); - return TRUE; + 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; + } } /* ----------------------------------------------------------------------