From 6859c4b6ca1ecef07a26e3779eb74817d1daa56f Mon Sep 17 00:00:00 2001 From: leitner Date: Tue, 29 Sep 2015 15:29:57 +0000 Subject: [PATCH] add netstring formatter fix scan_netstring to actually correctly return the length of the transported string add documentation --- fmt.h | 35 +++++++++++++++++++++++++++++++++++ fmt/fmt_netstring.3 | 29 +++++++++++++++++++++++++++++ fmt/fmt_netstring.c | 15 +++++++++++++++ scan/scan_netstring.3 | 27 +++++++++++++++++++++++++++ scan/scan_netstring.c | 2 +- test/netstring.c | 20 ++++++++++++++++++++ test/scan_netstring.c | 11 ----------- 7 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 fmt/fmt_netstring.3 create mode 100644 fmt/fmt_netstring.c create mode 100644 scan/scan_netstring.3 create mode 100644 test/netstring.c delete mode 100644 test/scan_netstring.c diff --git a/fmt.h b/fmt.h index 2a17a6c..6dbda3d 100644 --- a/fmt.h +++ b/fmt.h @@ -10,6 +10,8 @@ #include /* for byte_copy */ #include "byte.h" +/* for add_of */ +#include "rangecheck.h" #ifdef __cplusplus extern "C" { @@ -143,10 +145,43 @@ size_t fmt_iso8601(char* dest,time_t t); #define FMT_UTF8 5 #define FMT_ASN1LENGTH 17 /* enough space to hold 2^128-1 */ #define FMT_ASN1TAG 19 /* enough space to hold 2^128-1 */ + /* some variable length encodings for integers */ size_t fmt_utf8(char* dest,uint32_t n); /* can store 0-0x7fffffff */ size_t fmt_asn1derlength(char* dest,unsigned long long l); /* 0-0x7f: 1 byte, above that 1+bytes_needed bytes */ size_t fmt_asn1dertag(char* dest,unsigned long long l); /* 1 byte for each 7 bits; upper bit = more bytes coming */ +size_t fmt_varint(char* dest,unsigned long long l); /* protocol buffers encoding; like asn1dertag but little endian */ +size_t fmt_pb_tag(char* dest,size_t fieldno,unsigned char type); /* protocol buffer tag */ +size_t fmt_pb_type0_int(char* dest,signed long long l); /* protocol buffers encoding: type 0 integer */ +size_t fmt_pb_type1_double(char* dest,double d); /* protocol buffers encoding: double (64-bit little endian blob) */ +size_t fmt_pb_type1_fixed64(char* dest,uint64_t l); /* protocol buffers encoding: 64-bit little endian blob */ +size_t fmt_pb_type2_string(char* dest,const char* s,size_t l); /* protocol buffers encoding: varint length + blob */ +size_t fmt_pb_type5_float(char* dest,float f); /* protocol buffers encoding: float (32-bit little endian blob) */ +size_t fmt_pb_type5_fixed32(char* dest,uint32_t l); /* protocol buffers encoding: 32-bit little endian blob */ + +static inline size_t fmt_pb_int(char* dest,size_t fieldno,signed long long l) { + size_t n=fmt_pb_tag(dest,fieldno,0); + return n+fmt_pb_type0_int(dest?dest+n:0,l); +} + +static inline size_t fmt_pb_double(char* dest,size_t fieldno,double d) { + size_t n=fmt_pb_tag(dest,fieldno,1); + return n+fmt_pb_type1_double(dest?dest+n:0,d); +} + +static inline size_t fmt_pb_float(char* dest,size_t fieldno,float f) { + size_t n=fmt_pb_tag(dest,fieldno,5); + return n+fmt_pb_type5_float(dest?dest+n:0,f); +} + +static inline size_t fmt_pb_string(char* dest,size_t fieldno,const char* s,size_t l) { + size_t n=fmt_pb_tag(dest,fieldno,2); + size_t m; + if (add_of(m,fmt_pb_type2_string(NULL,s,l),n)) return 0; + return n+fmt_pb_type2_string(dest?dest+n:0,s,l); +} + +size_t fmt_netstring(char* dest,const char* src,size_t len); /* Marshaling helper functions. * Escape one character, no matter if it needs escaping or not. diff --git a/fmt/fmt_netstring.3 b/fmt/fmt_netstring.3 new file mode 100644 index 0000000..e85cb25 --- /dev/null +++ b/fmt/fmt_netstring.3 @@ -0,0 +1,29 @@ +.TH fmt_netstring 3 +.SH NAME +fmt_netstring \- convert a memory buffer into a netstring +.SH SYNTAX +.B #include + +size_t \fBfmt_netstring\fP(char *\fIdest\fR,const char *\fIsource\fR, + size_t \fIlen\fR); +.SH DESCRIPTION +fmt_netstring creates a netstring from a raw memory buffer and returns +the length. + +fmt_netstring does not append \\0. + +If \fIdest\fR equals FMT_LEN (i.e. is zero), fmt_netstring returns the number +of bytes it would have written. +.SH "RETURN VALUE" +fmt_netstring returns the number of bytes written (or that would have +been written, had the destination pointer not pointed to NULL). + +If the input buffer is implausibly large, fmt_netstring returns 0 +instead. +.SH EXAMPLE +The raw memory buffer "foo" would become the netstring "3:foo," +.SH SPEC +http://cr.yp.to/proto/netstrings.txt + +.SH "SEE ALSO" +scan_netstring(3) diff --git a/fmt/fmt_netstring.c b/fmt/fmt_netstring.c new file mode 100644 index 0000000..2da4c07 --- /dev/null +++ b/fmt/fmt_netstring.c @@ -0,0 +1,15 @@ +#include "fmt.h" +#include "rangecheck.h" +#include + +size_t fmt_netstring(char* dest,const char* src,size_t len) { + size_t n=fmt_ulong(NULL,len); + size_t m; + if (!range_validbuf(src,len) || add_of(m,len,n+2)) return 0; /* n came from fmt_ulong, so it is a very small number, below 50 */ + if (!dest) return m; + fmt_ulong(dest,len); dest+=n; + *dest++=':'; + memcpy(dest,src,len); + dest[len]=','; + return m; +} diff --git a/scan/scan_netstring.3 b/scan/scan_netstring.3 new file mode 100644 index 0000000..fb1ff06 --- /dev/null +++ b/scan/scan_netstring.3 @@ -0,0 +1,27 @@ +.TH scan_netstring 3 +.SH NAME +scan_netstring \- parse a netstring +.SH SYNTAX +.B #include + +size_t \fBscan_netstring\fP(const char *\fIin\fR,size_t len,char** \fIdest\fR, size_t *\fIslen\fR); +.SH DESCRIPTION +scan_netstring attempts to parse the netstring in the input buffer +(in,len). If the buffer contains a valid netstring, then (*dest,*slen) +is set to the start and length of the transported string. + +Note that this string is not zero terminated. No copy is made. +scan_netstring points it inside the input buffer. +.SH "RETURN VALUE" +scan_netstring returns the number of bytes in the netstring (the outer +representation, not the transported inner string) if parsing worked, or +0 if the input buffer did not contain a valid (or full) netstring. + +.SH EXAMPLE +The raw memory buffer "foo" would become the netstring "3:foo," +.SH SPEC +http://cr.yp.to/proto/netstrings.txt + +.SH "SEE ALSO" +fmt_netstring(3) + diff --git a/scan/scan_netstring.c b/scan/scan_netstring.c index 22ffbb3..d7d3e26 100644 --- a/scan/scan_netstring.c +++ b/scan/scan_netstring.c @@ -20,6 +20,6 @@ size_t scan_netstring(const char* in,size_t len,char** dest,size_t* slen) { in[n+l+1]!=',') return 0; *dest=(char*)in+n+1; - *slen=n; + *slen=l; return n+2+l; } diff --git a/test/netstring.c b/test/netstring.c new file mode 100644 index 0000000..7b5d0c2 --- /dev/null +++ b/test/netstring.c @@ -0,0 +1,20 @@ +#include "fmt.h" +#include "scan.h" +#include +#include + +int main() { + char buf[100]; + char* s; + size_t l; + const char* orig; + assert(fmt_netstring(buf,"hello world!",12)==16 && !memcmp(buf,"12:hello world!,",16)); + assert(scan_netstring(buf,16,&s,&l)==16 && s==buf+3 && l==12); + + orig="3:foo,"; assert(scan_netstring(orig,6,&s,&l)==6 && s==orig+2 && l==3); + orig="4294967295:foo,"; assert(scan_netstring(orig,15,&s,&l)==0); + orig="18446744073709551615:foo,"; assert(scan_netstring(orig,25,&s,&l)==0); + + assert(fmt_netstring(buf,orig,(size_t)-1)==0); + assert(fmt_netstring(buf,NULL,(size_t)-1)==0); +} diff --git a/test/scan_netstring.c b/test/scan_netstring.c deleted file mode 100644 index ac10e3b..0000000 --- a/test/scan_netstring.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "scan.h" -#include - -int main() { - char* s; - size_t l; - const char* orig; - orig="3:foo,"; assert(scan_netstring(orig,6,&s,&l)==6); assert(s==orig+2); - orig="4294967295:foo,"; assert(scan_netstring(orig,15,&s,&l)==0); - orig="18446744073709551615:foo,"; assert(scan_netstring(orig,25,&s,&l)==0); -}