зеркало из https://github.com/github/putty.git
191 строка
6.1 KiB
C
191 строка
6.1 KiB
C
/*
|
|
* Trivial binary packet protocol for the 'bare' ssh-connection
|
|
* protocol used in PuTTY's SSH-2 connection sharing system.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
|
|
#include "putty.h"
|
|
#include "ssh.h"
|
|
#include "sshbpp.h"
|
|
#include "sshcr.h"
|
|
|
|
struct ssh2_bare_bpp_state {
|
|
int crState;
|
|
long packetlen, maxlen;
|
|
unsigned char *data;
|
|
unsigned long incoming_sequence, outgoing_sequence;
|
|
PktIn *pktin;
|
|
|
|
BinaryPacketProtocol bpp;
|
|
};
|
|
|
|
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 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_bpp_queue_disconnect, /* in sshcommon.c */
|
|
0x4000, /* packet size limit, per protocol spec in sshshare.c comment */
|
|
};
|
|
|
|
BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx)
|
|
{
|
|
struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state);
|
|
memset(s, 0, sizeof(*s));
|
|
s->bpp.vt = &ssh2_bare_bpp_vtable;
|
|
s->bpp.logctx = logctx;
|
|
ssh_bpp_common_setup(&s->bpp);
|
|
return &s->bpp;
|
|
}
|
|
|
|
static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
|
|
{
|
|
struct ssh2_bare_bpp_state *s =
|
|
container_of(bpp, struct ssh2_bare_bpp_state, bpp);
|
|
sfree(s->pktin);
|
|
sfree(s);
|
|
}
|
|
|
|
#define BPP_READ(ptr, len) do \
|
|
{ \
|
|
bool success; \
|
|
crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \
|
|
s->bpp.in_raw, ptr, len)) || \
|
|
s->bpp.input_eof); \
|
|
if (!success) \
|
|
goto eof; \
|
|
ssh_check_frozen(s->bpp.ssh); \
|
|
} while (0)
|
|
|
|
static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
|
|
{
|
|
struct ssh2_bare_bpp_state *s =
|
|
container_of(bpp, struct ssh2_bare_bpp_state, bpp);
|
|
|
|
crBegin(s->crState);
|
|
|
|
while (1) {
|
|
/* Read the length field. */
|
|
{
|
|
unsigned char lenbuf[4];
|
|
BPP_READ(lenbuf, 4);
|
|
s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
|
|
}
|
|
|
|
if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
|
|
ssh_sw_abort(s->bpp.ssh, "Invalid packet length received");
|
|
crStopV;
|
|
}
|
|
|
|
/*
|
|
* Allocate the packet to return, now we know its length.
|
|
*/
|
|
s->pktin = snew_plus(PktIn, s->packetlen);
|
|
s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
|
|
s->pktin->qnode.on_free_queue = false;
|
|
s->maxlen = 0;
|
|
s->data = snew_plus_get_aux(s->pktin);
|
|
|
|
s->pktin->sequence = s->incoming_sequence++;
|
|
|
|
/*
|
|
* Read the remainder of the packet.
|
|
*/
|
|
BPP_READ(s->data, s->packetlen);
|
|
|
|
/*
|
|
* The data we just read is precisely the initial type byte
|
|
* followed by the packet payload.
|
|
*/
|
|
s->pktin->type = s->data[0];
|
|
s->data++;
|
|
s->packetlen--;
|
|
BinarySource_INIT(s->pktin, s->data, s->packetlen);
|
|
|
|
/*
|
|
* Log incoming packet, possibly omitting sensitive fields.
|
|
*/
|
|
if (s->bpp.logctx) {
|
|
logblank_t blanks[MAX_BLANKS];
|
|
int nblanks = ssh2_censor_packet(
|
|
s->bpp.pls, s->pktin->type, false,
|
|
make_ptrlen(s->data, s->packetlen), blanks);
|
|
log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
|
|
ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
|
|
s->pktin->type),
|
|
get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
|
|
&s->pktin->sequence, 0, NULL);
|
|
}
|
|
|
|
if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) {
|
|
sfree(s->pktin);
|
|
s->pktin = NULL;
|
|
continue;
|
|
}
|
|
|
|
s->pktin->qnode.formal_size = get_avail(s->pktin);
|
|
pq_push(&s->bpp.in_pq, s->pktin);
|
|
s->pktin = NULL;
|
|
}
|
|
|
|
eof:
|
|
if (!s->bpp.expect_close) {
|
|
ssh_remote_error(s->bpp.ssh,
|
|
"Remote side unexpectedly closed network connection");
|
|
} else {
|
|
ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
|
|
}
|
|
return; /* avoid touching s now it's been freed */
|
|
|
|
crFinishV;
|
|
}
|
|
|
|
static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type)
|
|
{
|
|
PktOut *pkt = ssh_new_packet();
|
|
pkt->length = 4; /* space for packet length */
|
|
pkt->type = pkt_type;
|
|
put_byte(pkt, pkt_type);
|
|
return pkt;
|
|
}
|
|
|
|
static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s,
|
|
PktOut *pkt)
|
|
{
|
|
if (s->bpp.logctx) {
|
|
ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5);
|
|
logblank_t blanks[MAX_BLANKS];
|
|
int nblanks = ssh2_censor_packet(
|
|
s->bpp.pls, pkt->type, true, pktdata, blanks);
|
|
log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
|
|
ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
|
|
pkt->type),
|
|
pktdata.ptr, pktdata.len, nblanks, blanks,
|
|
&s->outgoing_sequence,
|
|
pkt->downstream_id, pkt->additional_log_text);
|
|
}
|
|
|
|
s->outgoing_sequence++; /* only for diagnostics, really */
|
|
|
|
PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4);
|
|
bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
|
|
}
|
|
|
|
static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp)
|
|
{
|
|
struct ssh2_bare_bpp_state *s =
|
|
container_of(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);
|
|
}
|
|
}
|