sftp_packet_read: handle partial reads of the length field
SFTP packets come as [32 bit length][payload] and the code didn't previously handle that the initial 32 bit field was read only partially when it was read.
This commit is contained in:
parent
73be9fab04
commit
6395a738fd
72
src/sftp.c
72
src/sftp.c
@ -167,10 +167,8 @@ sftp_packet_read(LIBSSH2_SFTP *sftp)
|
|||||||
{
|
{
|
||||||
LIBSSH2_CHANNEL *channel = sftp->channel;
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
||||||
LIBSSH2_SESSION *session = channel->session;
|
LIBSSH2_SESSION *session = channel->session;
|
||||||
unsigned char buffer[4]; /* To store the packet length */
|
|
||||||
unsigned char *packet;
|
unsigned char *packet;
|
||||||
size_t packet_len, packet_received;
|
size_t packet_len, packet_received;
|
||||||
ssize_t bytes_received;
|
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
|
||||||
@ -187,62 +185,66 @@ sftp_packet_read(LIBSSH2_SFTP *sftp)
|
|||||||
"partial read cont, len: %lu", packet_len);
|
"partial read cont, len: %lu", packet_len);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rc = _libssh2_channel_read(channel, 0, (char *) buffer, 4);
|
/* each packet starts with a 32 bit length field */
|
||||||
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
rc = _libssh2_channel_read(channel, 0,
|
||||||
|
(char *)&sftp->partial_size[
|
||||||
|
sftp->partial_size_len],
|
||||||
|
4 - sftp->partial_size_len);
|
||||||
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
else if (rc < 0)
|
||||||
else if (4 != rc) {
|
return _libssh2_error(session, rc, "channel read");
|
||||||
/* TODO: this is stupid since we can in fact get 1-3 bytes in a
|
|
||||||
legitimate working case as well if the connection happens to be
|
sftp->partial_size_len += rc;
|
||||||
super slow or something */
|
|
||||||
return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
if(4 != sftp->partial_size_len)
|
||||||
"Read part of packet");
|
/* we got a short read for the length part */
|
||||||
}
|
return LIBSSH2_ERROR_EAGAIN;
|
||||||
|
|
||||||
|
packet_len = _libssh2_ntohu32(sftp->partial_size);
|
||||||
|
/* make sure we don't proceed if the packet size is unreasonably
|
||||||
|
large */
|
||||||
|
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN)
|
||||||
|
return _libssh2_error(session,
|
||||||
|
LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
|
||||||
|
"SFTP packet too large");
|
||||||
|
|
||||||
packet_len = _libssh2_ntohu32(buffer);
|
|
||||||
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
||||||
"Data begin - Packet Length: %lu", packet_len);
|
"Data begin - Packet Length: %lu", packet_len);
|
||||||
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
|
|
||||||
return _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
|
|
||||||
"SFTP packet too large");
|
|
||||||
}
|
|
||||||
|
|
||||||
packet = LIBSSH2_ALLOC(session, packet_len);
|
packet = LIBSSH2_ALLOC(session, packet_len);
|
||||||
if (!packet) {
|
if (!packet)
|
||||||
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
||||||
"Unable to allocate SFTP packet");
|
"Unable to allocate SFTP packet");
|
||||||
}
|
sftp->partial_size_len = 0;
|
||||||
|
|
||||||
packet_received = 0;
|
packet_received = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read as much of the packet as we can */
|
/* Read as much of the packet as we can */
|
||||||
while (packet_len > packet_received) {
|
while (packet_len > packet_received) {
|
||||||
bytes_received =
|
rc = _libssh2_channel_read(channel, 0,
|
||||||
_libssh2_channel_read(channel, 0,
|
(char *) packet + packet_received,
|
||||||
(char *) packet + packet_received,
|
packet_len - packet_received);
|
||||||
packet_len - packet_received);
|
|
||||||
|
|
||||||
if (bytes_received == LIBSSH2_ERROR_EAGAIN) {
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
||||||
/*
|
/*
|
||||||
* We received EAGAIN, save what we have and
|
* We received EAGAIN, save what we have and return EAGAIN to the
|
||||||
* return to EAGAIN to the caller
|
* caller
|
||||||
*/
|
*/
|
||||||
sftp->partial_packet = packet;
|
sftp->partial_packet = packet;
|
||||||
sftp->partial_len = packet_len;
|
sftp->partial_len = packet_len;
|
||||||
sftp->partial_received = packet_received;
|
sftp->partial_received = packet_received;
|
||||||
packet = NULL;
|
return rc;
|
||||||
|
|
||||||
return bytes_received;
|
|
||||||
}
|
}
|
||||||
else if (bytes_received < 0) {
|
else if (rc < 0) {
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
return _libssh2_error(session, bytes_received,
|
return _libssh2_error(session, rc,
|
||||||
"Receive error waiting for SFTP packet");
|
"Error waiting for SFTP packet");
|
||||||
}
|
}
|
||||||
packet_received += bytes_received;
|
packet_received += rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sftp->partial_packet = NULL;
|
||||||
|
|
||||||
rc = sftp_packet_add(sftp, packet, packet_len);
|
rc = sftp_packet_add(sftp, packet, packet_len);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
LIBSSH2_FREE(session, packet);
|
LIBSSH2_FREE(session, packet);
|
||||||
|
@ -114,6 +114,8 @@ struct _LIBSSH2_SFTP
|
|||||||
uint32_t last_errno;
|
uint32_t last_errno;
|
||||||
|
|
||||||
/* Holder for partial packet, use in libssh2_sftp_packet_read() */
|
/* Holder for partial packet, use in libssh2_sftp_packet_read() */
|
||||||
|
unsigned char partial_size[4]; /* buffer for size field */
|
||||||
|
size_t partial_size_len; /* size field length */
|
||||||
unsigned char *partial_packet; /* The data */
|
unsigned char *partial_packet; /* The data */
|
||||||
size_t partial_len; /* Desired number of bytes */
|
size_t partial_len; /* Desired number of bytes */
|
||||||
size_t partial_received; /* Bytes received so far */
|
size_t partial_received; /* Bytes received so far */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user