summaryrefslogtreecommitdiff
path: root/modules/ctrl_tcp/netstring
diff options
context:
space:
mode:
authorJosé Luis Millán <jmillan@aliax.net>2018-01-29 15:21:39 +0100
committerAlfred E. Heggestad <alfred.heggestad@gmail.com>2018-01-29 15:21:39 +0100
commit376796ffe68fb078c77ee7da435acb23fa6a3b3d (patch)
treecddd039aacb9238d9abc949836fa1364321592ae /modules/ctrl_tcp/netstring
parentdce37b83eaa6fd543664128bd75f680b418f0d41 (diff)
ctrl_tcp module (#346)
* ctrl_tcp module * ua: set a default reason for UA_EVENT_CALL_CLOSED on user hangup * ctrl_tcp: netstring * ctrl_tcp: fix typo * ctrl_tcp: avoid compiler warnings * ctrl_tcp: fix memory leak * ctrl_tcp: make linter happy * ctrl_tcp: make linter happy * ctrl_tcp: enhance message definition * ctrl_tcp: work on command error and empty response * ctrl_tcp: add response message attribute 'ok' * ctrl_tcp: cleanup * ctrl_tcp: linter. Fix too wide line * ctrl_tcp: Fix. remove unused variable * ctrl_tcp: rename 'netstring-c' folder by 'netstring' * ctrl_tcp: rename 'netstring.[ch]' by 'tcp_netstring.[ch]' * ctrl_tcp: reformat 'netstring' code * ctrl_tcp: move 'netstring_error_str' function to 'netstring' code * ctrl_tcp: remove cstring README file * ctrl_tcp: cleanup cstring makefile * ctrl_tcp: remove makefile and testsuite files from 'netstring' * ctrl_tcp: move variable decalaration to the beginning of the function * ctrl_tcp: add copyright headers
Diffstat (limited to 'modules/ctrl_tcp/netstring')
-rw-r--r--modules/ctrl_tcp/netstring/netstring.c163
-rw-r--r--modules/ctrl_tcp/netstring/netstring.h28
2 files changed, 191 insertions, 0 deletions
diff --git a/modules/ctrl_tcp/netstring/netstring.c b/modules/ctrl_tcp/netstring/netstring.c
new file mode 100644
index 0000000..d429791
--- /dev/null
+++ b/modules/ctrl_tcp/netstring/netstring.c
@@ -0,0 +1,163 @@
+/* Streaming API for netstrings. */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <math.h>
+#include "netstring.h"
+
+
+const char* netstring_error_str(netstring_error err)
+{
+ switch (err) {
+ case NETSTRING_ERROR_TOO_LONG:
+ return "NETSTRING_ERROR_TOO_LONG";
+ case NETSTRING_ERROR_NO_COLON:
+ return "NETSTRING_ERROR_NO_COLON";
+ case NETSTRING_ERROR_TOO_SHORT:
+ return "NETSTRING_ERROR_TOO_SHORT";
+ case NETSTRING_ERROR_NO_COMMA:
+ return "NETSTRING_ERROR_NO_COMMA";
+ case NETSTRING_ERROR_LEADING_ZERO:
+ return "NETSTRING_ERROR_LEADING_ZERO";
+ case NETSTRING_ERROR_NO_LENGTH:
+ return "NETSTRING_ERROR_NO_LENGTH";
+ default:
+ return "NETSTRING_ERROR_UNKNOWN";
+ }
+}
+
+
+/**
+ * Reads a netstring from a `buffer` of length `buffer_length`. Writes
+ * to `netstring_start` a pointer to the beginning of the string in
+ * the buffer, and to `netstring_length` the length of the
+ * string. Does not allocate any memory. If it reads successfully,
+ * then it returns 0. If there is an error, then the return value will
+ * be negative. The error values are:
+
+ * NETSTRING_ERROR_TOO_LONG More than 999999999 bytes in a field
+ * NETSTRING_ERROR_NO_COLON No colon was found after the number
+ * NETSTRING_ERROR_TOO_SHORT Number of bytes greater than buffer length
+ * NETSTRING_ERROR_NO_COMMA No comma was found at the end
+ * NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed
+ * NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring
+
+ * If you're sending messages with more than 999999999 bytes -- about
+ * 2 GB -- then you probably should not be doing so in the form of a
+ * single netstring. This restriction is in place partially to protect
+ * from malicious or erroneous input, and partly to be compatible with
+ * D. J. Bernstein's reference implementation.
+
+ * Example:
+ * if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die();
+ */
+int netstring_read(char *buffer, size_t buffer_length,
+ char **netstring_start, size_t *netstring_length)
+{
+ size_t i;
+ size_t len = 0;
+
+ /* Write default values for outputs */
+ *netstring_start = NULL; *netstring_length = 0;
+
+ /* Make sure buffer is big enough. Minimum size is 3. */
+ if (buffer_length < 3)
+ return NETSTRING_ERROR_TOO_SHORT;
+
+ /* No leading zeros allowed! */
+ if (buffer[0] == '0' && isdigit(buffer[1]))
+ return NETSTRING_ERROR_LEADING_ZERO;
+
+ /* The netstring must start with a number */
+ if (!isdigit(buffer[0]))
+ return NETSTRING_ERROR_NO_LENGTH;
+
+ /* Read the number of bytes */
+ for (i = 0; i < buffer_length && isdigit(buffer[i]); i++) {
+
+ /* Error if more than 9 digits */
+ if (i >= 9)
+ return NETSTRING_ERROR_TOO_LONG;
+
+ /* Accumulate each digit, assuming ASCII. */
+ len = len*10 + (buffer[i] - '0');
+ }
+
+ /**
+ * Check buffer length. The buffer must be longer than the sum of:
+ * - the number we've read.
+ * - the length of the string itself.
+ * - the colon.
+ * - the comma.
+ */
+ if (i + len + 1 >= buffer_length)
+ return NETSTRING_ERROR_TOO_SHORT;
+
+ /* Read the colon */
+ if (buffer[i++] != ':')
+ return NETSTRING_ERROR_NO_COLON;
+
+ /* Test for the trailing comma, and set the return values */
+ if (buffer[i + len] != ',')
+ return NETSTRING_ERROR_NO_COMMA;
+
+ *netstring_start = &buffer[i]; *netstring_length = len;
+
+ return 0;
+}
+
+/**
+ * Return the number of digits represented in the given number.
+ * We are assuming that the input is not bigger than NETSTRING_MAX_SIZE.
+ */
+size_t netstring_num_len(size_t num)
+{
+ char num_str[10];
+
+ sprintf(num_str, "%zu", num);
+
+ return strlen(num_str);
+}
+
+/**
+ * Return the length, in ASCII characters, of a netstring containing
+ * `data_length` bytes.
+ */
+size_t netstring_buffer_size(size_t data_length)
+{
+ if (data_length == 0)
+ return 3;
+
+ return netstring_num_len(data_length) + data_length + 2;
+}
+
+/*
+ * Allocate and create a netstring containing the first `len` bytes of `data`.
+ * This must be manually freed by the client.
+ * If `len` is 0 then no data will be read from `data`, and it may be NULL.
+ */
+size_t netstring_encode_new(char **netstring, char *data, size_t len)
+{
+ char *ns;
+ size_t num_len = 1;
+
+ if (len == 0) {
+ ns = malloc(3);
+ ns[0] = '0';
+ ns[1] = ':';
+ ns[2] = ',';
+ }
+ else {
+ num_len = netstring_num_len(len);
+ ns = malloc(num_len + len + 2);
+ sprintf(ns, "%lu:", (unsigned long)len);
+ memcpy(ns + num_len + 1, data, len);
+ ns[num_len + len + 1] = ',';
+ }
+
+ *netstring = ns;
+
+ return num_len + len + 2;
+}
diff --git a/modules/ctrl_tcp/netstring/netstring.h b/modules/ctrl_tcp/netstring/netstring.h
new file mode 100644
index 0000000..a084428
--- /dev/null
+++ b/modules/ctrl_tcp/netstring/netstring.h
@@ -0,0 +1,28 @@
+#ifndef __NETSTRING_STREAM_H
+#define __NETSTRING_STREAM_H
+
+#include <string.h>
+
+const char* netstring_error_str(int err);
+
+int netstring_read(char *buffer, size_t buffer_length,
+ char **netstring_start, size_t *netstring_length);
+
+size_t netstring_num_len(size_t num);
+size_t netstring_buffer_size(size_t data_length);
+
+size_t netstring_encode_new(char **netstring, char *data, size_t len);
+
+#define NETSTRING_MAX_SIZE 999999999
+
+/* Errors that can occur during netstring parsing */
+typedef enum {
+ NETSTRING_ERROR_TOO_LONG = -100,
+ NETSTRING_ERROR_NO_COLON,
+ NETSTRING_ERROR_TOO_SHORT,
+ NETSTRING_ERROR_NO_COMMA,
+ NETSTRING_ERROR_LEADING_ZERO,
+ NETSTRING_ERROR_NO_LENGTH
+} netstring_error;
+
+#endif