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

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.
This commit is contained in:
Simon Tatham 2018-09-24 18:08:09 +01:00
parent 60d95b6a62
commit 6bb847738b
7 changed files with 173 additions and 72 deletions

68
ssh.c
View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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");