├── makefile ├── netstring.h ├── README.md ├── netstring.c └── testsuite.c /makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -O2 -Wall 2 | LDFLAGS= -lm 3 | 4 | .PHONY: test clean 5 | 6 | all: testsuite 7 | 8 | testsuite: testsuite.c netstring.o 9 | 10 | test: testsuite 11 | ./testsuite 12 | 13 | clean: 14 | rm -f *.o testsuite 15 | -------------------------------------------------------------------------------- /netstring.h: -------------------------------------------------------------------------------- 1 | #ifndef __NETSTRING_STREAM_H 2 | #define __NETSTRING_STREAM_H 3 | 4 | #include 5 | 6 | int netstring_read(char *buffer, size_t buffer_length, 7 | char **netstring_start, size_t *netstring_length); 8 | 9 | size_t netstring_buffer_size(size_t data_length); 10 | 11 | size_t netstring_encode_new(char **netstring, char *data, size_t len); 12 | 13 | /* Errors that can occur during netstring parsing */ 14 | #define NETSTRING_ERROR_TOO_LONG -1 15 | #define NETSTRING_ERROR_NO_COLON -2 16 | #define NETSTRING_ERROR_TOO_SHORT -3 17 | #define NETSTRING_ERROR_NO_COMMA -4 18 | #define NETSTRING_ERROR_LEADING_ZERO -5 19 | #define NETSTRING_ERROR_NO_LENGTH -6 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Netstring parsing 2 | ================= 3 | 4 | A [netstring](http://en.wikipedia.org/wiki/Netstring) is a way of encoding a sequence of bytes for transmission over a network, or for serialization. They're very easy to work with. They encode the data's length, and can be concatenated trivially. The format was [defined by D. J. Bernstein](http://cr.yp.to/proto/netstrings.txt) and is used in various software. Examples of netstrings: 5 | 6 | "12:hello, world!," 7 | "3:foo," 8 | "0:," 9 | 10 | To specify a list of strings, just concatenate the netstrings. The list ["hey", "everyone"] can be encoded as 11 | 12 | "3:hey,8:everyone," 13 | 14 | This is some code in C for netstring serialization and deserialization. It checks netstrings for validity when parsing them, and guards against malicious input. It's also fast. 15 | 16 | Basic API 17 | --------- 18 | 19 | All the code is in `netstring.c` and `netstring.h`, and these have no external dependencies. To use them, just include them in your application. Include `netstring.h` and link with the C code. 20 | 21 | **Parsing netstrings** 22 | 23 | To parse a netstring, use `netstring_read()`. 24 | 25 | int netstring_read(char *buffer, size_t buffer_length, 26 | char **netstring_start, size_t *netstring_length); 27 | 28 | Reads a netstring from a `buffer` of length `buffer_length`. Writes to 29 | `netstring_start` a pointer to the beginning of the string in the 30 | buffer, and to `netstring_length` the length of the string. Does not 31 | allocate any memory. If it reads successfully, then it returns 0. If 32 | there is an error, then the return value will be negative. The error 33 | values are: 34 | 35 | NETSTRING_ERROR_TOO_LONG More than 999999999 bytes in a field 36 | NETSTRING_ERROR_NO_COLON No colon was found after the number 37 | NETSTRING_ERROR_TOO_SHORT Number of bytes greater than buffer length 38 | NETSTRING_ERROR_NO_COMMA No comma was found at the end 39 | NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed 40 | NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring 41 | 42 | If you're sending messages with more than 999999999 bytes -- about 2 43 | GB -- then you probably should not be doing so in the form of a single 44 | netstring. This restriction is in place partially to protect from 45 | malicious or erroneous input, and partly to be compatible with 46 | D. J. Bernstein's reference implementation. 47 | 48 | **Creating netstrings** 49 | 50 | To create a netstring, there are a few ways to do it. You could do something really simple like this example from [the spec](http://cr.yp.to/proto/netstrings.txt): 51 | 52 | if (printf("%lu:", len) < 0) barf(); 53 | if (fwrite(buf, 1, len, stdout) < len) barf(); 54 | if (putchar(',') < 0) barf(); 55 | 56 | This code provides a convenience function for creating a new netstring: 57 | 58 | size_t netstring_encode_new(char **netstring, char *data, size_t len); 59 | 60 | This allocates and creates 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. 61 | 62 | Contributing 63 | ------------ 64 | 65 | All this code is Public Domain. If you want to contribute, you can send bug reports, or fork the project on GitHub. Contributions are welcomed with open arms. -------------------------------------------------------------------------------- /netstring.c: -------------------------------------------------------------------------------- 1 | /* Streaming API for netstrings. */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "netstring.h" 9 | 10 | /* Reads a netstring from a `buffer` of length `buffer_length`. Writes 11 | to `netstring_start` a pointer to the beginning of the string in 12 | the buffer, and to `netstring_length` the length of the 13 | string. Does not allocate any memory. If it reads successfully, 14 | then it returns 0. If there is an error, then the return value will 15 | be negative. The error values are: 16 | 17 | NETSTRING_ERROR_TOO_LONG More than 999999999 bytes in a field 18 | NETSTRING_ERROR_NO_COLON No colon was found after the number 19 | NETSTRING_ERROR_TOO_SHORT Number of bytes greater than buffer length 20 | NETSTRING_ERROR_NO_COMMA No comma was found at the end 21 | NETSTRING_ERROR_LEADING_ZERO Leading zeros are not allowed 22 | NETSTRING_ERROR_NO_LENGTH Length not given at start of netstring 23 | 24 | If you're sending messages with more than 999999999 bytes -- about 25 | 2 GB -- then you probably should not be doing so in the form of a 26 | single netstring. This restriction is in place partially to protect 27 | from malicious or erroneous input, and partly to be compatible with 28 | D. J. Bernstein's reference implementation. 29 | 30 | Example: 31 | if (netstring_read("3:foo,", 6, &str, &len) < 0) explode_and_die(); 32 | */ 33 | int netstring_read(char *buffer, size_t buffer_length, 34 | char **netstring_start, size_t *netstring_length) { 35 | int i; 36 | size_t len = 0; 37 | 38 | /* Write default values for outputs */ 39 | *netstring_start = NULL; *netstring_length = 0; 40 | 41 | /* Make sure buffer is big enough. Minimum size is 3. */ 42 | if (buffer_length < 3) return NETSTRING_ERROR_TOO_SHORT; 43 | 44 | /* No leading zeros allowed! */ 45 | if (buffer[0] == '0' && isdigit(buffer[1])) 46 | return NETSTRING_ERROR_LEADING_ZERO; 47 | 48 | /* The netstring must start with a number */ 49 | if (!isdigit(buffer[0])) return NETSTRING_ERROR_NO_LENGTH; 50 | 51 | /* Read the number of bytes */ 52 | for (i = 0; i < buffer_length && isdigit(buffer[i]); i++) { 53 | /* Error if more than 9 digits */ 54 | if (i >= 9) return NETSTRING_ERROR_TOO_LONG; 55 | /* Accumulate each digit, assuming ASCII. */ 56 | len = len*10 + (buffer[i] - '0'); 57 | } 58 | 59 | /* Check buffer length once and for all. Specifically, we make sure 60 | that the buffer is longer than the number we've read, the length 61 | of the string itself, and the colon and comma. */ 62 | if (i + len + 1 >= buffer_length) return NETSTRING_ERROR_TOO_SHORT; 63 | 64 | /* Read the colon */ 65 | if (buffer[i++] != ':') return NETSTRING_ERROR_NO_COLON; 66 | 67 | /* Test for the trailing comma, and set the return values */ 68 | if (buffer[i + len] != ',') return NETSTRING_ERROR_NO_COMMA; 69 | *netstring_start = &buffer[i]; *netstring_length = len; 70 | 71 | return 0; 72 | } 73 | 74 | /* Return the length, in ASCII characters, of a netstring containing 75 | `data_length` bytes. */ 76 | size_t netstring_buffer_size(size_t data_length) { 77 | if (data_length == 0) return 3; 78 | return (size_t)ceil(log10((double)data_length + 1)) + data_length + 2; 79 | } 80 | 81 | /* Allocate and create a netstring containing the first `len` bytes of 82 | `data`. This must be manually freed by the client. If `len` is 0 83 | then no data will be read from `data`, and it may be NULL. */ 84 | size_t netstring_encode_new(char **netstring, char *data, size_t len) { 85 | char *ns; 86 | size_t num_len = 1; 87 | 88 | if (len == 0) { 89 | ns = malloc(3); 90 | ns[0] = '0'; 91 | ns[1] = ':'; 92 | ns[2] = ','; 93 | } else { 94 | num_len = (size_t)ceil(log10((double)len + 1)); 95 | ns = malloc(num_len + len + 2); 96 | sprintf(ns, "%lu:", (unsigned long)len); 97 | memcpy(ns + num_len + 1, data, len); 98 | ns[num_len + len + 1] = ','; 99 | } 100 | 101 | *netstring = ns; 102 | return num_len + len + 2; 103 | } 104 | 105 | -------------------------------------------------------------------------------- /testsuite.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "netstring.h" 7 | 8 | 9 | /* Good examples */ 10 | char ex1[] = "12:hello world!,"; 11 | char ex2[] = "3:foo,0:,3:bar,"; 12 | 13 | /* Bad examples */ 14 | char ex3[] = "12:hello world! "; /* No comma */ 15 | char ex4[] = "15:hello world!,"; /* Too short */ 16 | char ex5[] = "03:foo,"; /* Leading zeros are forbidden */ 17 | char ex6[] = "999999999999999:haha lol,"; /* Too long */ 18 | char ex7[] = "3fool,"; /* No colon */ 19 | char ex8[] = "what's up"; /* No number or colon */ 20 | char ex9[] = ":what's up"; /* No number */ 21 | 22 | 23 | void test_netstring_read(void) { 24 | char *netstring; 25 | size_t netstring_len; 26 | int retval; 27 | 28 | /* ex1: hello world */ 29 | retval = netstring_read(ex1, strlen(ex1), &netstring, &netstring_len); 30 | assert(netstring_len == 12); assert(strncmp(netstring, "hello world!", 12) == 0); 31 | assert(retval == 0); 32 | 33 | 34 | /* ex2: three netstrings, concatenated. */ 35 | 36 | retval = netstring_read(ex2, strlen(ex2), &netstring, &netstring_len); 37 | assert(netstring_len == 3); assert(strncmp(netstring, "foo", 3) == 0); 38 | assert(retval == 0); 39 | 40 | retval = netstring_read(netstring+netstring_len+1, 9, &netstring, &netstring_len); 41 | assert(netstring_len == 0); assert(retval == 0); 42 | 43 | retval = netstring_read(netstring+netstring_len+1, 6, &netstring, &netstring_len); 44 | assert(netstring_len == 3); assert(strncmp(netstring, "bar", 3) == 0); 45 | assert(retval == 0); 46 | 47 | 48 | /* ex3: no comma */ 49 | retval = netstring_read(ex3, strlen(ex3), &netstring, &netstring_len); 50 | assert(netstring_len == 0); assert(netstring == NULL); 51 | assert(retval == NETSTRING_ERROR_NO_COMMA); 52 | 53 | /* ex4: too short */ 54 | retval = netstring_read(ex4, strlen(ex4), &netstring, &netstring_len); 55 | assert(netstring_len == 0); assert(netstring == NULL); 56 | assert(retval == NETSTRING_ERROR_TOO_SHORT); 57 | 58 | /* ex5: leading zero */ 59 | retval = netstring_read(ex5, strlen(ex5), &netstring, &netstring_len); 60 | assert(netstring_len == 0); assert(netstring == NULL); 61 | assert(retval == NETSTRING_ERROR_LEADING_ZERO); 62 | 63 | /* ex6: too long */ 64 | retval = netstring_read(ex6, strlen(ex6), &netstring, &netstring_len); 65 | assert(netstring_len == 0); assert(netstring == NULL); 66 | assert(retval == NETSTRING_ERROR_TOO_LONG); 67 | 68 | /* ex7: no colon */ 69 | retval = netstring_read(ex7, strlen(ex7), &netstring, &netstring_len); 70 | assert(netstring_len == 0); assert(netstring == NULL); 71 | assert(retval == NETSTRING_ERROR_NO_COLON); 72 | 73 | /* ex8: no number or colon */ 74 | retval = netstring_read(ex8, strlen(ex8), &netstring, &netstring_len); 75 | assert(netstring_len == 0); assert(netstring == NULL); 76 | assert(retval == NETSTRING_ERROR_NO_LENGTH); 77 | 78 | /* ex9: no number */ 79 | retval = netstring_read(ex9, strlen(ex9), &netstring, &netstring_len); 80 | assert(netstring_len == 0); assert(netstring == NULL); 81 | assert(retval == NETSTRING_ERROR_NO_LENGTH); 82 | } 83 | 84 | void test_netstring_buffer_size(void) { 85 | assert(netstring_buffer_size(0) == 3); 86 | assert(netstring_buffer_size(1) == 4); 87 | assert(netstring_buffer_size(2) == 5); 88 | assert(netstring_buffer_size(9) == 12); 89 | assert(netstring_buffer_size(10) == 14); 90 | assert(netstring_buffer_size(12345) == 12345 + 5 + 2); 91 | } 92 | 93 | void test_netstring_encode_new(void) { 94 | char *ns; size_t bytes; 95 | 96 | bytes = netstring_encode_new(&ns, "foo", 3); 97 | assert(ns != NULL); assert(strncmp(ns, "3:foo,", 6) == 0); assert(bytes == 6); 98 | free(ns); 99 | 100 | bytes = netstring_encode_new(&ns, NULL, 0); 101 | assert(ns != NULL); assert(strncmp(ns, "0:,", 3) == 0); assert(bytes == 3); 102 | free(ns); 103 | 104 | bytes = netstring_encode_new(&ns, "hello world!", 12); assert(bytes == 16); 105 | assert(ns != NULL); assert(strncmp(ns, "12:hello world!,", 16) == 0); 106 | free(ns); 107 | } 108 | 109 | int main(void) { 110 | printf("Running test suite...\n"); 111 | test_netstring_read(); 112 | test_netstring_buffer_size(); 113 | test_netstring_encode_new(); 114 | printf("All tests passed!\n"); 115 | return 0; 116 | } 117 | --------------------------------------------------------------------------------