diff options
author | José Luis Millán <jmillan@aliax.net> | 2018-01-29 15:21:39 +0100 |
---|---|---|
committer | Alfred E. Heggestad <alfred.heggestad@gmail.com> | 2018-01-29 15:21:39 +0100 |
commit | 376796ffe68fb078c77ee7da435acb23fa6a3b3d (patch) | |
tree | cddd039aacb9238d9abc949836fa1364321592ae /modules/ctrl_tcp/netstring | |
parent | dce37b83eaa6fd543664128bd75f680b418f0d41 (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.c | 163 | ||||
-rw-r--r-- | modules/ctrl_tcp/netstring/netstring.h | 28 |
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 |