1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
/**
* @file netstring.c Streaming API for netstrings
*
* This file is public domain,
* adapted from https://github.com/PeterScott/netstring-c/"
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <re.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[32];
re_snprintf(num_str, sizeof(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;
}
|