From 5ede32a826fa0c27a82cea51063ebf5050a8c1d5 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 21 Oct 2010 21:00:28 +0200 Subject: [PATCH] _libssh2_transport_write: remade to send without malloc --- src/comp.c | 184 +++++++++++++-------------------------------- src/comp.h | 4 +- src/kex.c | 21 +++--- src/libssh2_priv.h | 18 +++-- src/session.c | 5 -- src/transport.c | 58 ++++++-------- 6 files changed, 102 insertions(+), 188 deletions(-) diff --git a/src/comp.c b/src/comp.c index c4e8c81..07ad86a 100644 --- a/src/comp.c +++ b/src/comp.c @@ -53,7 +53,30 @@ * Minimalist compression: Absolutely none */ static int -comp_method_none_comp(LIBSSH2_SESSION * session, +comp_method_none_comp(LIBSSH2_SESSION *session, + unsigned char *dest, + size_t *dest_len, + const unsigned char *src, + size_t src_len, + void **abstract) +{ + (void) session; + (void) abstract; + (void) dest; + (void) dest_len; + (void) src; + (void) src_len; + + return 0; +} + +/* + * comp_method_none_decomp + * + * Minimalist decompression: Absolutely none + */ +static int +comp_method_none_decomp(LIBSSH2_SESSION * session, int compress, unsigned char **dest, size_t *dest_len, @@ -81,7 +104,7 @@ static const LIBSSH2_COMP_METHOD comp_method_none = { 0, /* not really compressing */ NULL, comp_method_none_comp, - comp_method_none_comp, + comp_method_none_decomp, NULL }; @@ -154,141 +177,35 @@ comp_method_zlib_init(LIBSSH2_SESSION * session, int compress, /* * libssh2_comp_method_zlib_comp * - * Compresses source to destination. Without any allocation. + * Compresses source to destination. Without allocation. */ static int -comp_method_zlib_comp(LIBSSH2_SESSION * session, - int compress, - unsigned char **dest, +comp_method_zlib_comp(LIBSSH2_SESSION *session, + unsigned char *dest, + + /* dest_len is a pointer to allow this function to + update it with the final actual size used */ size_t *dest_len, - size_t payload_limit, - int *free_dest, const unsigned char *src, - size_t src_len, void **abstract) + size_t src_len, + void **abstract) { z_stream *strm = *abstract; - /* A short-term alloc of a full data chunk is better than a series of - reallocs */ - char *out; - int out_maxlen = compress ? (src_len + 4) : (2 * src_len); - int limiter = 0; - - /* If strm is null, then we have not yet been initialized. */ - if (strm == NULL) { - *dest = (unsigned char *) src; - *dest_len = src_len; - - *free_dest = 0; - return 0; - } - - /* In practice they never come smaller than this */ - if (out_maxlen < 25) { - out_maxlen = 25; - } - - if (out_maxlen > (int) payload_limit) { - out_maxlen = payload_limit; - } + int out_maxlen = *dest_len; + int status; strm->next_in = (unsigned char *) src; strm->avail_in = src_len; - strm->next_out = (unsigned char *) LIBSSH2_ALLOC(session, out_maxlen); - out = (char *) strm->next_out; + strm->next_out = dest; strm->avail_out = out_maxlen; - if (!strm->next_out) { - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to allocate compression/decompression " - "buffer"); - } - while (strm->avail_in) { - int status; - if (compress) { - status = deflate(strm, Z_PARTIAL_FLUSH); - } else { - status = inflate(strm, Z_PARTIAL_FLUSH); - } - if (status != Z_OK) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "compress/decompression failure"); - } - if (strm->avail_in) { - size_t out_ofs = out_maxlen - strm->avail_out; - char *newout; + status = deflate(strm, Z_PARTIAL_FLUSH); - out_maxlen += - compress ? (strm->avail_in + 4) : (2 * strm->avail_in); + if (status != Z_OK) + return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, + "compression failure"); - if ((out_maxlen > (int) payload_limit) && !compress && limiter++) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "Excessive growth in decompression phase"); - } - - newout = LIBSSH2_REALLOC(session, out, out_maxlen); - if (!newout) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to expand compress/" - "decompression buffer"); - } - out = newout; - strm->next_out = (unsigned char *) out + out_ofs; - strm->avail_out += - compress ? (strm->avail_in + 4) : (2 * strm->avail_in); - } else - while (!strm->avail_out) { - /* Done with input, might be a byte or two in internal buffer - * during compress. Or potentially many bytes if it's a - * decompress - */ - int grow_size = compress ? 8 : 1024; - char *newout; - - if (out_maxlen >= (int) payload_limit) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "Excessive growth in decompression " - "phase"); - } - - if (grow_size > (int) (payload_limit - out_maxlen)) { - grow_size = payload_limit - out_maxlen; - } - - out_maxlen += grow_size; - strm->avail_out = grow_size; - - newout = LIBSSH2_REALLOC(session, out, out_maxlen); - if (!newout) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ALLOC, - "Unable to expand final compress/" - "decompress buffer"); - } - out = newout; - strm->next_out = (unsigned char *) out + out_maxlen - - grow_size; - - if (compress) { - status = deflate(strm, Z_PARTIAL_FLUSH); - } else { - status = inflate(strm, Z_PARTIAL_FLUSH); - } - if (status != Z_OK) { - LIBSSH2_FREE(session, out); - return _libssh2_error(session, LIBSSH2_ERROR_ZLIB, - "compress/decompression failure"); - } - } - } - - *dest = (unsigned char *) out; *dest_len = out_maxlen - strm->avail_out; - *free_dest = 1; - return 0; } @@ -438,7 +355,7 @@ comp_method_zlib_decomp(LIBSSH2_SESSION * session, * All done, no more compression for you */ static int -comp_method_zlib_dtor(LIBSSH2_SESSION * session, int compress, +comp_method_zlib_dtor(LIBSSH2_SESSION *session, int compress, void **abstract) { z_stream *strm = *abstract; @@ -470,10 +387,8 @@ static const LIBSSH2_COMP_METHOD comp_method_zlib = { }; #endif /* LIBSSH2_HAVE_ZLIB */ -/* *********************** - * Compression Methods * - *********************** */ - +/* If compression is enabled by the API, then this array is used which then + may allow compression if zlib is available at build time */ static const LIBSSH2_COMP_METHOD *comp_methods[] = { #ifdef LIBSSH2_HAVE_ZLIB &comp_method_zlib, @@ -482,8 +397,17 @@ static const LIBSSH2_COMP_METHOD *comp_methods[] = { NULL }; +/* If compression is disabled by the API, then this array is used */ +static const LIBSSH2_COMP_METHOD *no_comp_methods[] = { + &comp_method_none, + NULL +}; + const LIBSSH2_COMP_METHOD ** -_libssh2_comp_methods(void) +_libssh2_comp_methods(LIBSSH2_SESSION *session) { - return comp_methods; + if(session->flag.compress) + return comp_methods; + else + return no_comp_methods; } diff --git a/src/comp.h b/src/comp.h index 119abc8..8edc150 100644 --- a/src/comp.h +++ b/src/comp.h @@ -1,7 +1,7 @@ #ifndef __LIBSSH2_COMP_H #define __LIBSSH2_COMP_H -/* Copyright (C) 2009 by Daniel Stenberg +/* Copyright (C) 2009-2010 by Daniel Stenberg * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided @@ -40,6 +40,6 @@ #include "libssh2_priv.h" -const LIBSSH2_COMP_METHOD **_libssh2_comp_methods(void); +const LIBSSH2_COMP_METHOD **_libssh2_comp_methods(LIBSSH2_SESSION *session); #endif /* __LIBSSH2_COMP_H */ diff --git a/src/kex.c b/src/kex.c index 2a5ebc2..23ce7dd 100644 --- a/src/kex.c +++ b/src/kex.c @@ -1,4 +1,5 @@ /* Copyright (c) 2004-2007, Sara Golemon + * Copyright (c) 2010, Daniel Stenberg * All rights reserved. * * Redistribution and use in source and binary forms, @@ -1043,10 +1044,10 @@ static int kexinit(LIBSSH2_SESSION * session) _libssh2_mac_methods()); comp_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.comp_prefs, - _libssh2_comp_methods()); + _libssh2_comp_methods(session)); comp_sc_len = LIBSSH2_METHOD_PREFS_LEN(session->remote.comp_prefs, - _libssh2_comp_methods()); + _libssh2_comp_methods(session)); lang_cs_len = LIBSSH2_METHOD_PREFS_LEN(session->local.lang_prefs, NULL); lang_sc_len = @@ -1083,9 +1084,9 @@ static int kexinit(LIBSSH2_SESSION * session) LIBSSH2_METHOD_PREFS_STR(s, mac_sc_len, session->remote.mac_prefs, _libssh2_mac_methods()); LIBSSH2_METHOD_PREFS_STR(s, comp_cs_len, session->local.comp_prefs, - _libssh2_comp_methods()); + _libssh2_comp_methods(session)); LIBSSH2_METHOD_PREFS_STR(s, comp_sc_len, session->remote.comp_prefs, - _libssh2_comp_methods()); + _libssh2_comp_methods(session)); LIBSSH2_METHOD_PREFS_STR(s, lang_cs_len, session->local.lang_prefs, NULL); LIBSSH2_METHOD_PREFS_STR(s, lang_sc_len, session->remote.lang_prefs, @@ -1487,11 +1488,11 @@ static int kex_agree_mac(LIBSSH2_SESSION * session, /* kex_agree_comp * Agree on a compression scheme */ -static int kex_agree_comp(LIBSSH2_SESSION * session, - libssh2_endpoint_data * endpoint, unsigned char *comp, +static int kex_agree_comp(LIBSSH2_SESSION *session, + libssh2_endpoint_data *endpoint, unsigned char *comp, unsigned long comp_len) { - const LIBSSH2_COMP_METHOD **compp = _libssh2_comp_methods(); + const LIBSSH2_COMP_METHOD **compp = _libssh2_comp_methods(session); unsigned char *s; (void) session; @@ -1827,12 +1828,14 @@ libssh2_session_method_pref(LIBSSH2_SESSION * session, int method_type, case LIBSSH2_METHOD_COMP_CS: prefvar = &session->local.comp_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(); + mlist = (const LIBSSH2_COMMON_METHOD **) + _libssh2_comp_methods(session); break; case LIBSSH2_METHOD_COMP_SC: prefvar = &session->remote.comp_prefs; - mlist = (const LIBSSH2_COMMON_METHOD **) _libssh2_comp_methods(); + mlist = (const LIBSSH2_COMMON_METHOD **) + _libssh2_comp_methods(session); break; case LIBSSH2_METHOD_LANG_CS: diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 7942d2b..4994a1d 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -492,8 +492,8 @@ struct transportpacket are currently writing decrypted data */ /* ------------- for outgoing data --------------- */ - unsigned char *outbuf; /* pointer to a LIBSSH2_ALLOC() area for the - outgoing data */ + unsigned char outbuf[MAX_SSH_PACKET_LEN]; /* area for the outgoing data */ + int ototal_num; /* size of outbuf in number of bytes */ unsigned char *odata; /* original pointer to the data we stored in outbuf */ @@ -1009,12 +1009,14 @@ struct _LIBSSH2_COMP_METHOD { const char *name; int compress; /* 1 if it does compress, 0 if it doesn't */ - int (*init) (LIBSSH2_SESSION * session, int compress, void **abstract); - int (*comp) (LIBSSH2_SESSION * session, int compress, unsigned char **dest, - size_t *dest_len, size_t payload_limit, - int *free_dest, const unsigned char *src, - size_t src_len, void **abstract); - int (*decomp) (LIBSSH2_SESSION * session, int compress, + int (*init) (LIBSSH2_SESSION *session, int compress, void **abstract); + int (*comp) (LIBSSH2_SESSION *session, + unsigned char *dest, + size_t *dest_len, + const unsigned char *src, + size_t src_len, + void **abstract); + int (*decomp) (LIBSSH2_SESSION *session, int compress, unsigned char **dest, size_t *dest_len, size_t payload_limit, int *free_dest, const unsigned char *src, diff --git a/src/session.c b/src/session.c index 855923a..679b9c9 100644 --- a/src/session.c +++ b/src/session.c @@ -980,11 +980,6 @@ session_free(LIBSSH2_SESSION *session) LIBSSH2_FREE(session, pkg); } - /* Cleanup remaining outgoing packet buffer */ - if (p->outbuf) { - LIBSSH2_FREE(session, p->outbuf); - } - if(session->socket_prev_blockstate) /* if the socket was previously blocking, put it back so */ session_nonblock(session->socket_fd, 0); diff --git a/src/transport.c b/src/transport.c index 56dfca9..f531027 100644 --- a/src/transport.c +++ b/src/transport.c @@ -200,13 +200,13 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ ) size_t data_len; int free_payload = 1; - rc = session->remote.comp->comp(session, 0, - &data, &data_len, - LIBSSH2_PACKET_MAXDECOMP, - &free_payload, - p->payload, - session->fullpacket_payload_len, - &session->remote.comp_abstract); + rc = session->remote.comp->decomp(session, 0, + &data, &data_len, + LIBSSH2_PACKET_MAXDECOMP, + &free_payload, + p->payload, + session->fullpacket_payload_len, + &session->remote.comp_abstract); if(rc) { LIBSSH2_FREE(session, p->payload); return rc; @@ -605,7 +605,7 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, ssize_t length; struct transportpacket *p = &session->packet; - if (!p->outbuf) { + if (!p->olen) { *ret = 0; return LIBSSH2_ERROR_NONE; } @@ -642,8 +642,6 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, if (rc == length) { /* the remainder of the package was sent */ - LIBSSH2_FREE(session, p->outbuf); - p->outbuf = NULL; p->ototal_num = 0; } else if (rc < 0) { @@ -673,10 +671,6 @@ send_existing(LIBSSH2_SESSION * session, unsigned char *data, * then be called with the same argument set (same data pointer and same * data_len) until ERROR_NONE or failure is returned. * - * NOTE: this function does not verify that 'data_len' is less than ~35000 - * which is what all implementations should support at least as packet size. - * (RFC4253 section 6.1) - * * This function DOES not call _libssh2_error() on any errors. */ int @@ -689,7 +683,6 @@ _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, int padding_length; int packet_length; int total_length; - int free_data = 0; #ifdef RANDOM_PADDING int rand_max; int seed = data[0]; /* FIXME: make this random */ @@ -702,6 +695,11 @@ _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, unsigned char *orgdata = data; size_t orgdata_len = data_len; + if(data_len >= MAX_SSH_PACKET_LEN) + /* too large packet, return error for this until we make this function + split it up and send multiple SSH packets */ + return LIBSSH2_ERROR_INVAL; + debugdump(session, "libssh2_transport_write plain", data, data_len); /* FIRST, check if we have a pending write to complete */ @@ -717,15 +715,21 @@ _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0; - /* check if we should compress */ if (encrypted && session->local.comp->compress) { - rc = session->local.comp->comp(session, 1, &data, &data_len, - LIBSSH2_PACKET_MAXCOMP, - &free_data, data, data_len, + /* compress directly to the target buffer */ + rc = session->local.comp->comp(session, + data, &data_len, + &p->outbuf[5], MAX_SSH_PACKET_LEN-5, &session->local.comp_abstract); if(rc) return rc; /* compression failure */ + + data = p->outbuf; + /* data_len is already updated by the compression function */ } + else + /* copy the payload data */ + memcpy(&p->outbuf[5], data, data_len); /* RFC4253 says: Note that the length of the concatenation of 'packet_length', 'padding_length', 'payload', and 'random padding' @@ -766,26 +770,14 @@ _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, total_length = packet_length + (encrypted ? session->local.mac->mac_len : 0); - /* allocate memory to store the outgoing packet in, in case we can't - send the whole one and thus need to keep it after this function - returns. */ - p->outbuf = LIBSSH2_ALLOC(session, total_length); - if (!p->outbuf) { - return LIBSSH2_ERROR_ALLOC; - } - /* store packet_length, which is the size of the whole packet except the MAC and the packet_length field itself */ _libssh2_htonu32(p->outbuf, packet_length - 4); /* store padding_length */ p->outbuf[4] = padding_length; - /* copy the payload data */ - memcpy(p->outbuf + 5, data, data_len); + /* fill the padding area with random junk */ _libssh2_random(p->outbuf + 5 + data_len, padding_length); - if (free_data) { - LIBSSH2_FREE(session, data); - } if (encrypted) { /* Calculate MAC hash. Put the output at index packet_length, @@ -837,8 +829,6 @@ _libssh2_transport_write(LIBSSH2_SESSION * session, unsigned char *data, /* the whole thing got sent away */ p->odata = NULL; p->olen = 0; - LIBSSH2_FREE(session, p->outbuf); - p->outbuf = NULL; return LIBSSH2_ERROR_NONE; /* all is good */ }