aboutsummaryrefslogtreecommitdiff
path: root/src/buffer.c
diff options
context:
space:
mode:
authorAndreas Schneider <asn@cryptomilk.org>2018-08-31 17:48:42 +0200
committerAndreas Schneider <asn@cryptomilk.org>2018-09-03 18:32:12 +0200
commitc15bd2831f72c2a0bb1abdbfc171bcea06fc13ed (patch)
treee1b20f2061e9b180ddc8d73135dd816f2f28f032 /src/buffer.c
parentefef8773567753ca24e676c8744c8bfccda629ad (diff)
downloadlibssh-c15bd2831f72c2a0bb1abdbfc171bcea06fc13ed.tar.gz
libssh-c15bd2831f72c2a0bb1abdbfc171bcea06fc13ed.tar.xz
libssh-c15bd2831f72c2a0bb1abdbfc171bcea06fc13ed.zip
buffer: Precalculate the size required for ssh_buffer_pack()
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Diffstat (limited to 'src/buffer.c')
-rw-r--r--src/buffer.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 9e7d267e..8cc01c74 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -786,6 +786,122 @@ struct ssh_string_struct *ssh_buffer_get_ssh_string(struct ssh_buffer_struct *bu
return str;
}
+/**
+ * @brief Pre-calculate the size we need for packing the buffer.
+ *
+ * This makes sure that enough memory is allocated for packing the buffer and
+ * we only have to do one memory allocation.
+ *
+ * @param[in] buffer The buffer to allocate
+ *
+ * @param[in] format A format string of arguments.
+ *
+ * @param[in] argc The number of arguments.
+ *
+ * @param[in] ap The va_list of arguments.
+ *
+ * @return SSH_OK on success, SSH_ERROR on error.
+ */
+static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
+ const char *format,
+ int argc,
+ va_list ap)
+{
+ const char *p = NULL;
+ ssh_string string = NULL;
+ char *cstring = NULL;
+ size_t needed_size = 0;
+ size_t count;
+ size_t len;
+ int rc = SSH_OK;
+
+ for (p = format, count = 0; *p != '\0'; p++, count++) {
+ /* Invalid number of arguments passed */
+ if (argc != -1 && count > argc) {
+ return SSH_ERROR;
+ }
+
+ switch(*p) {
+ case 'b':
+ va_arg(ap, unsigned int);
+ needed_size += sizeof(uint8_t);
+ break;
+ case 'w':
+ va_arg(ap, unsigned int);
+ needed_size += sizeof(uint16_t);
+ break;
+ case 'd':
+ va_arg(ap, uint32_t);
+ needed_size += sizeof(uint32_t);
+ break;
+ case 'q':
+ va_arg(ap, uint64_t);
+ needed_size += sizeof(uint64_t);
+ break;
+ case 'S':
+ string = va_arg(ap, ssh_string);
+ needed_size += 4 + ssh_string_len(string);
+ string = NULL;
+ break;
+ case 's':
+ cstring = va_arg(ap, char *);
+ needed_size += sizeof(uint32_t) + strlen(cstring);
+ cstring = NULL;
+ break;
+ case 'P':
+ len = va_arg(ap, size_t);
+ needed_size += len;
+ va_arg(ap, void *);
+ count++; /* increase argument count */
+ break;
+ case 'B':
+ va_arg(ap, bignum);
+ /*
+ * Use a fixed size for a bignum
+ * (they should normaly be around 32)
+ */
+ needed_size += 64;
+ break;
+ case 't':
+ cstring = va_arg(ap, char *);
+ needed_size += strlen(cstring);
+ cstring = NULL;
+ break;
+ default:
+ SSH_LOG(SSH_LOG_WARN, "Invalid buffer format %c", *p);
+ rc = SSH_ERROR;
+ }
+ if (rc != SSH_OK){
+ break;
+ }
+ }
+
+ if (argc != -1 && argc != count) {
+ return SSH_ERROR;
+ }
+
+ if (rc != SSH_ERROR){
+ /*
+ * Check if our canary is intact, if not, something really bad happened.
+ */
+ uint32_t canary = va_arg(ap, uint32_t);
+ if (canary != SSH_BUFFER_PACK_END) {
+ if (argc == -1){
+ return SSH_ERROR;
+ } else {
+ abort();
+ }
+ }
+ }
+
+ rc = ssh_buffer_allocate_size(buffer, needed_size);
+ if (rc != 0) {
+ return SSH_ERROR;
+ }
+
+ return SSH_OK;
+}
+
/** @internal
* @brief Add multiple values in a buffer on a single function call
* @param[in] buffer The buffer to add to
@@ -936,8 +1052,17 @@ int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
int rc;
va_start(ap, argc);
+ rc = ssh_buffer_pack_allocate_va(buffer, format, argc, ap);
+ va_end(ap);
+
+ if (rc != SSH_OK) {
+ return rc;
+ }
+
+ va_start(ap, argc);
rc = ssh_buffer_pack_va(buffer, format, argc, ap);
va_end(ap);
+
return rc;
}