From 6bb847738bf3aa6f56f93cc9e8cedc9f0207663a Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Mon, 24 Sep 2018 18:08:09 +0100 Subject: [PATCH] Give the BPP an input and output packet queue. Now, instead of writing each packet straight on to the raw output bufchain by calling the BPP's format_packet function, the higher protocol layers will put the packets on to a queue, which will automatically trigger a callback (using the new mechanism for embedding a callback in any packet queue) to make the BPP format its queue on to the raw-output bufchain. That in turn triggers a second callback which moves the data to the socket. This means in particular that the CBC ignore-message workaround can be moved into the new BPP routine to process the output queue, which is a good place for it because then it can easily arrange to only put an ignore message at the start of any sequence of packets that are being formatted as a single output blob. --- ssh.c | 68 +++++++++++++++++++++++--------------------------- ssh1bpp.c | 23 +++++++++++------ ssh2bpp-bare.c | 27 +++++++++++++------- ssh2bpp.c | 55 ++++++++++++++++++++++++++++++++++------ sshbpp.h | 30 +++++++++++++++++----- sshcommon.c | 35 ++++++++++++++++++++++++-- sshverstring.c | 7 +++--- 7 files changed, 173 insertions(+), 72 deletions(-) diff --git a/ssh.c b/ssh.c index 92a93e27..b4ad1a76 100644 --- a/ssh.c +++ b/ssh.c @@ -474,8 +474,7 @@ struct ssh_tag { int incoming_data_seen_eof; char *incoming_data_eof_message; - PktInQueue pq_full; - struct IdempotentCallback pq_full_consumer; + struct IdempotentCallback incoming_pkt_consumer; PktInQueue pq_ssh1_login; struct IdempotentCallback ssh1_login_icb; @@ -725,22 +724,7 @@ static int s_write(Ssh ssh, const void *data, int len) static void ssh_pkt_write(Ssh ssh, PktOut *pkt) { - if (ssh->version == 2 && ssh->v2_cbc_ignore_workaround && - bufchain_size(&ssh->outgoing_data) != 0) { - /* - * When using a CBC-mode cipher in SSH-2, it's necessary to - * ensure that an attacker can't provide data to be encrypted - * using an IV that they know. We ensure this by prefixing - * each packet that might contain user data with an - * SSH_MSG_IGNORE. - */ - PktOut *ipkt = ssh_bpp_new_pktout(ssh->bpp, SSH2_MSG_IGNORE); - put_stringz(ipkt, ""); - ssh_bpp_format_packet(ssh->bpp, ipkt); - } - - ssh_bpp_format_packet(ssh->bpp, pkt); - queue_idempotent_callback(&ssh->outgoing_data_sender); + pq_push(&ssh->bpp->out_pq, pkt); } /* @@ -881,8 +865,6 @@ static void ssh2_add_sigblob(Ssh ssh, PktOut *pkt, static void ssh_feed_to_bpp(Ssh ssh) { - PacketQueueNode *prev_tail = ssh->pq_full.pqb.end.prev; - assert(ssh->bpp); ssh_bpp_handle_input(ssh->bpp); @@ -904,9 +886,6 @@ static void ssh_feed_to_bpp(Ssh ssh) ssh->close_expected = TRUE; ssh->disconnect_message_seen = TRUE; } - - if (ssh->pq_full.pqb.end.prev != prev_tail) - queue_idempotent_callback(&ssh->pq_full_consumer); } static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, @@ -976,12 +955,14 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, } ssh->bpp->out_raw = &ssh->outgoing_data; + ssh->bpp->out_raw->ic = &ssh->outgoing_data_sender; ssh->bpp->in_raw = &ssh->incoming_data; - ssh->bpp->in_pq = &ssh->pq_full; + ssh->bpp->in_pq.pqb.ic = &ssh->incoming_pkt_consumer; ssh->bpp->pls = &ssh->pls; ssh->bpp->logctx = ssh->logctx; + ssh->bpp->remote_bugs = ssh->remote_bugs; - queue_idempotent_callback(&ssh->incoming_data_consumer); + queue_idempotent_callback(&ssh->bpp->ic_in_raw); queue_idempotent_callback(&ssh->user_input_consumer); update_specials_menu(ssh->frontend); @@ -1034,12 +1015,12 @@ static void ssh_process_incoming_data(void *ctx) } } -static void ssh_process_pq_full(void *ctx) +static void ssh_process_incoming_pkts(void *ctx) { Ssh ssh = (Ssh)ctx; PktIn *pktin; - while ((pktin = pq_pop(&ssh->pq_full)) != NULL) { + while ((pktin = pq_pop(&ssh->bpp->in_pq)) != NULL) { if (ssh->general_packet_processing) ssh->general_packet_processing(ssh, pktin); ssh->packet_dispatch[pktin->type](ssh, pktin); @@ -1730,6 +1711,13 @@ static void do_ssh1_login(void *vctx) sfree(s->rsabuf); + /* + * Force the BPP to synchronously marshal all packets up to and + * including the SESSION_KEY into wire format, before we turn on + * crypto. + */ + ssh_bpp_handle_output(ssh->bpp); + { const struct ssh1_cipheralg *cipher = (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh1_blowfish : @@ -5077,6 +5065,13 @@ static void do_ssh2_transport(void *vctx) ssh->stats.out.running = TRUE; ssh->stats.out.remaining = ssh->max_data_size; + /* + * Force the BPP to synchronously marshal all packets up to and + * including that NEWKEYS into wire format, before we switch over + * to new crypto. + */ + ssh_bpp_handle_output(ssh->bpp); + /* * We've sent client NEWKEYS, so create and initialise * client-to-server session keys. @@ -6616,10 +6611,10 @@ static void ssh2_msg_userauth(Ssh ssh, PktIn *pktin) * protocol has officially started, which means we must * install the dispatch-table entries for all the * connection-layer messages. In particular, we must do this - * _before_ we return to the loop in ssh_process_pq_full + * _before_ we return to the loop in ssh_process_incoming_pkts * that's processing the currently queued packets through the * dispatch table, because if (say) an SSH_MSG_GLOBAL_REQUEST - * is already pending in pq_full, we can't afford to delay + * is already pending in in_pq, we can't afford to delay * installing its dispatch table entry until after that queue * run is done. */ @@ -8331,14 +8326,15 @@ static void do_ssh2_connection(void *vctx) /* * Put our current pending packet queue back to the front of - * pq_full, and then schedule a callback to re-process those + * the main pq, and then schedule a callback to re-process those * packets (if any). That way, anything already in our queue that * 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_peek(&ssh->pq_ssh2_connection)) { - pq_concatenate(&ssh->pq_full, &ssh->pq_ssh2_connection, &ssh->pq_full); - queue_idempotent_callback(&ssh->pq_full_consumer); + pq_concatenate(&ssh->bpp->in_pq, + &ssh->pq_ssh2_connection, &ssh->bpp->in_pq); + queue_idempotent_callback(ssh->bpp->in_pq.pqb.ic); } /* @@ -9005,10 +9001,9 @@ static const char *ssh_init(Frontend *frontend, Backend **backend_handle, ssh->incoming_data_consumer.fn = ssh_process_incoming_data; ssh->incoming_data_consumer.ctx = ssh; ssh->incoming_data_consumer.queued = FALSE; - pq_in_init(&ssh->pq_full); - ssh->pq_full_consumer.fn = ssh_process_pq_full; - ssh->pq_full_consumer.ctx = ssh; - ssh->pq_full_consumer.queued = FALSE; + ssh->incoming_pkt_consumer.fn = ssh_process_incoming_pkts; + ssh->incoming_pkt_consumer.ctx = ssh; + ssh->incoming_pkt_consumer.queued = FALSE; pq_in_init(&ssh->pq_ssh1_login); ssh->ssh1_login_icb.fn = do_ssh1_login; ssh->ssh1_login_icb.ctx = ssh; @@ -9191,7 +9186,6 @@ static void ssh_free(Backend *be) bufchain_clear(&ssh->incoming_data); bufchain_clear(&ssh->outgoing_data); sfree(ssh->incoming_data_eof_message); - pq_in_clear(&ssh->pq_full); pq_in_clear(&ssh->pq_ssh1_login); pq_in_clear(&ssh->pq_ssh1_connection); pq_in_clear(&ssh->pq_ssh2_transport); diff --git a/ssh1bpp.c b/ssh1bpp.c index 9640d35d..24eaeab4 100644 --- a/ssh1bpp.c +++ b/ssh1bpp.c @@ -30,14 +30,14 @@ struct ssh1_bpp_state { static void ssh1_bpp_free(BinaryPacketProtocol *bpp); static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp); +static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh1_bpp_new_pktout(int type); -static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt); static const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = { ssh1_bpp_free, ssh1_bpp_handle_input, + ssh1_bpp_handle_output, ssh1_bpp_new_pktout, - ssh1_bpp_format_packet, }; BinaryPacketProtocol *ssh1_bpp_new(void) @@ -45,6 +45,7 @@ BinaryPacketProtocol *ssh1_bpp_new(void) struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state); memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh1_bpp_vtable; + ssh_bpp_common_setup(&s->bpp); return &s->bpp; } @@ -202,7 +203,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp) NULL, 0, NULL); } - pq_push(s->bpp.in_pq, s->pktin); + pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; @@ -241,9 +242,8 @@ static PktOut *ssh1_bpp_new_pktout(int pkt_type) return pkt; } -static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) +static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt) { - struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp); int pad, biglen, i, pktoffs; unsigned long crc; int len; @@ -290,6 +290,15 @@ static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) bufchain_add(s->bpp.out_raw, pkt->data + pktoffs, biglen + 4); /* len(length+padding+type+data+CRC) */ - - ssh_free_pktout(pkt); +} + +static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp) +{ + struct ssh1_bpp_state *s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp); + PktOut *pkt; + + while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { + ssh1_bpp_format_packet(s, pkt); + ssh_free_pktout(pkt); + } } diff --git a/ssh2bpp-bare.c b/ssh2bpp-bare.c index 58e6ae32..1c838303 100644 --- a/ssh2bpp-bare.c +++ b/ssh2bpp-bare.c @@ -22,14 +22,14 @@ struct ssh2_bare_bpp_state { static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp); static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp); +static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bare_bpp_new_pktout(int type); -static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *); static const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = { ssh2_bare_bpp_free, ssh2_bare_bpp_handle_input, + ssh2_bare_bpp_handle_output, ssh2_bare_bpp_new_pktout, - ssh2_bare_bpp_format_packet, }; BinaryPacketProtocol *ssh2_bare_bpp_new(void) @@ -37,6 +37,7 @@ BinaryPacketProtocol *ssh2_bare_bpp_new(void) struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state); memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh2_bare_bpp_vtable; + ssh_bpp_common_setup(&s->bpp); return &s->bpp; } @@ -120,7 +121,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp) continue; } - pq_push(s->bpp.in_pq, s->pktin); + pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; @@ -142,11 +143,9 @@ static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type) return pkt; } -static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) +static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s, + PktOut *pkt) { - struct ssh2_bare_bpp_state *s = - FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp); - if (s->bpp.logctx) { ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5); logblank_t blanks[MAX_BLANKS]; @@ -164,6 +163,16 @@ static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) PUT_32BIT(pkt->data, pkt->length - 4); bufchain_add(s->bpp.out_raw, pkt->data, pkt->length); - - ssh_free_pktout(pkt); +} + +static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp) +{ + struct ssh2_bare_bpp_state *s = + FROMFIELD(bpp, struct ssh2_bare_bpp_state, bpp); + PktOut *pkt; + + while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { + ssh2_bare_bpp_format_packet(s, pkt); + ssh_free_pktout(pkt); + } } diff --git a/ssh2bpp.c b/ssh2bpp.c index 21ea2401..1bc27340 100644 --- a/ssh2bpp.c +++ b/ssh2bpp.c @@ -25,6 +25,7 @@ struct ssh2_bpp_state { unsigned cipherblk; PktIn *pktin; struct DataTransferStats *stats; + int cbc_ignore_workaround; struct ssh2_bpp_direction in, out; /* comp and decomp logically belong in the per-direction @@ -39,14 +40,14 @@ struct ssh2_bpp_state { static void ssh2_bpp_free(BinaryPacketProtocol *bpp); static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp); +static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bpp_new_pktout(int type); -static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt); static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = { ssh2_bpp_free, ssh2_bpp_handle_input, + ssh2_bpp_handle_output, ssh2_bpp_new_pktout, - ssh2_bpp_format_packet, }; BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats) @@ -55,6 +56,7 @@ BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats) memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh2_bpp_vtable; s->stats = stats; + ssh_bpp_common_setup(&s->bpp); return &s->bpp; } @@ -99,8 +101,13 @@ void ssh2_bpp_new_outgoing_crypto( s->out.cipher = ssh2_cipher_new(cipher); ssh2_cipher_setkey(s->out.cipher, ckey); ssh2_cipher_setiv(s->out.cipher, iv); + + s->cbc_ignore_workaround = ( + (ssh2_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) && + !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)); } else { s->out.cipher = NULL; + s->cbc_ignore_workaround = FALSE; } s->out.etm_mode = etm_mode; if (mac) { @@ -478,7 +485,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp) continue; } - pq_push(s->bpp.in_pq, s->pktin); + pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; @@ -611,10 +618,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt) } -static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) +static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt) { - struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp); - if (pkt->minlen > 0 && !s->out_comp) { /* * If we've been told to pad the packet out to a given minimum @@ -678,6 +683,40 @@ static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt) ssh2_bpp_format_packet_inner(s, pkt); bufchain_add(s->bpp.out_raw, pkt->data, pkt->length); - - ssh_free_pktout(pkt); +} + +static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp) +{ + struct ssh2_bpp_state *s = FROMFIELD(bpp, struct ssh2_bpp_state, bpp); + PktOut *pkt; + + if (s->cbc_ignore_workaround) { + /* + * When using a CBC-mode cipher in SSH-2, it's necessary to + * ensure that an attacker can't provide data to be encrypted + * using an IV that they know. We ensure this by inserting an + * SSH_MSG_IGNORE if the last cipher block of the previous + * packet has already been sent to the network (which we + * approximate conservatively by checking if it's vanished + * from out_raw). + */ + if (bufchain_size(s->bpp.out_raw) < + (ssh2_cipher_alg(s->out.cipher)->blksize + + ssh2_mac_alg(s->out.mac)->len)) { + /* + * There's less data in out_raw than the MAC size plus the + * cipher block size, which means at least one byte of + * that cipher block must already have left. Add an + * IGNORE. + */ + pkt = ssh_bpp_new_pktout(&s->bpp, SSH2_MSG_IGNORE); + put_stringz(pkt, ""); + ssh2_bpp_format_packet(s, pkt); + } + } + + while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { + ssh2_bpp_format_packet(s, pkt); + ssh_free_pktout(pkt); + } } diff --git a/sshbpp.h b/sshbpp.h index 61f4fe7f..c3f26728 100644 --- a/sshbpp.h +++ b/sshbpp.h @@ -5,30 +5,44 @@ #ifndef PUTTY_SSHBPP_H #define PUTTY_SSHBPP_H -typedef struct BinaryPacketProtocol BinaryPacketProtocol; - struct BinaryPacketProtocolVtable { void (*free)(BinaryPacketProtocol *); void (*handle_input)(BinaryPacketProtocol *); + void (*handle_output)(BinaryPacketProtocol *); PktOut *(*new_pktout)(int type); - void (*format_packet)(BinaryPacketProtocol *, PktOut *); }; struct BinaryPacketProtocol { const struct BinaryPacketProtocolVtable *vt; bufchain *in_raw, *out_raw; - PktInQueue *in_pq; + PktInQueue in_pq; + PktOutQueue out_pq; PacketLogSettings *pls; LogContext *logctx; + Ssh ssh; + + /* ic_in_raw is filled in by the BPP (probably by calling + * ssh_bpp_common_setup). The BPP's owner triggers it when data is + * added to in_raw, and also when the BPP is newly created. */ + IdempotentCallback ic_in_raw; + + /* ic_out_pq is entirely internal to the BPP itself; it's used as + * the callback on out_pq. */ + IdempotentCallback ic_out_pq; + + int remote_bugs; int seen_disconnect; char *error; }; -#define ssh_bpp_free(bpp) ((bpp)->vt->free(bpp)) #define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp)) +#define ssh_bpp_handle_output(bpp) ((bpp)->vt->handle_output(bpp)) #define ssh_bpp_new_pktout(bpp, type) ((bpp)->vt->new_pktout(type)) -#define ssh_bpp_format_packet(bpp, pkt) ((bpp)->vt->format_packet(bpp, pkt)) + +/* ssh_bpp_free is more than just a macro wrapper on the vtable; it + * does centralised parts of the freeing too. */ +void ssh_bpp_free(BinaryPacketProtocol *bpp); BinaryPacketProtocol *ssh1_bpp_new(void); void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, @@ -40,6 +54,10 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, * up zlib compression if it was SUCCESS. */ void ssh1_bpp_requested_compression(BinaryPacketProtocol *bpp); +/* Helper routine which does common BPP initialisation, e.g. setting + * up in_pq and out_pq, and initialising input_consumer. */ +void ssh_bpp_common_setup(BinaryPacketProtocol *); + /* Common helper function between the SSH-2 full and bare BPPs */ int ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin); diff --git a/sshcommon.c b/sshcommon.c index 932e6c46..088e1c88 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -252,6 +252,7 @@ void ssh_free_pktout(PktOut *pkt) sfree(pkt->data); sfree(pkt); } + /* ---------------------------------------------------------------------- * Implement zombiechan_new() and its trivial vtable. */ @@ -630,9 +631,39 @@ const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) #undef TRANSLATE_AUTH /* ---------------------------------------------------------------------- - * Common helper function for implementations of BinaryPacketProtocol. + * Common helper functions for clients and implementations of + * BinaryPacketProtocol. */ +static void ssh_bpp_input_raw_data_callback(void *context) +{ + BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context; + ssh_bpp_handle_input(bpp); +} + +static void ssh_bpp_output_packet_callback(void *context) +{ + BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context; + ssh_bpp_handle_output(bpp); +} + +void ssh_bpp_common_setup(BinaryPacketProtocol *bpp) +{ + pq_in_init(&bpp->in_pq); + pq_out_init(&bpp->out_pq); + bpp->ic_in_raw.fn = ssh_bpp_input_raw_data_callback; + bpp->ic_in_raw.ctx = bpp; + bpp->ic_out_pq.fn = ssh_bpp_output_packet_callback; + bpp->ic_out_pq.ctx = bpp; + bpp->out_pq.pqb.ic = &bpp->ic_out_pq; +} + +void ssh_bpp_free(BinaryPacketProtocol *bpp) +{ + delete_callbacks_for_context(bpp); + bpp->vt->free(bpp); +} + #define BITMAP_UNIVERSAL(y, name, value) \ | (value >= y && value < y+32 ? 1UL << (value-y) : 0) #define BITMAP_CONDITIONAL(y, name, value, ctx) \ @@ -658,7 +689,7 @@ int ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin) !((valid_bitmap[pktin->type >> 5] >> (pktin->type & 0x1F)) & 1)) { PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH2_MSG_UNIMPLEMENTED); put_uint32(pkt, pktin->sequence); - ssh_bpp_format_packet(bpp, pkt); + pq_push(&bpp->out_pq, pkt); return TRUE; } diff --git a/sshverstring.c b/sshverstring.c index 029b140c..f02b0e9b 100644 --- a/sshverstring.c +++ b/sshverstring.c @@ -41,14 +41,14 @@ struct ssh_verstring_state { static void ssh_verstring_free(BinaryPacketProtocol *bpp); static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp); +static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh_verstring_new_pktout(int type); -static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *); static const struct BinaryPacketProtocolVtable ssh_verstring_vtable = { ssh_verstring_free, ssh_verstring_handle_input, + ssh_verstring_handle_output, ssh_verstring_new_pktout, - ssh_verstring_format_packet, }; static void ssh_detect_bugs(struct ssh_verstring_state *s); @@ -97,6 +97,7 @@ BinaryPacketProtocol *ssh_verstring_new( s->send_early = !ssh_version_includes_v1(protoversion); s->bpp.vt = &ssh_verstring_vtable; + ssh_bpp_common_setup(&s->bpp); return &s->bpp; } @@ -394,7 +395,7 @@ static PktOut *ssh_verstring_new_pktout(int type) return NULL; } -static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *pkg) +static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp) { assert(0 && "Should never try to send packets during SSH version " "string exchange");