1 /* $NetBSD: netstring.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* netstring 3 6 /* SUMMARY 7 /* netstring stream I/O support 8 /* SYNOPSIS 9 /* #include <netstring.h> 10 /* 11 /* void netstring_setup(stream, timeout) 12 /* VSTREAM *stream; 13 /* int timeout; 14 /* 15 /* void netstring_except(stream, exception) 16 /* VSTREAM *stream; 17 /* int exception; 18 /* 19 /* VSTRING *netstring_get(stream, buf, limit) 20 /* VSTREAM *stream; 21 /* VSTRING *buf; 22 /* ssize_t limit; 23 /* 24 /* void netstring_put(stream, data, len) 25 /* VSTREAM *stream; 26 /* const char *data; 27 /* ssize_t len; 28 /* 29 /* void netstring_put_multi(stream, data, len, data, len, ..., 0) 30 /* VSTREAM *stream; 31 /* const char *data; 32 /* ssize_t len; 33 /* 34 /* void NETSTRING_PUT_BUF(stream, buf) 35 /* VSTREAM *stream; 36 /* VSTRING *buf; 37 /* 38 /* void netstring_fflush(stream) 39 /* VSTREAM *stream; 40 /* 41 /* VSTRING *netstring_memcpy(buf, data, len) 42 /* VSTRING *buf; 43 /* const char *data; 44 /* ssize_t len; 45 /* 46 /* VSTRING *netstring_memcat(buf, data, len) 47 /* VSTRING *buf; 48 /* const char *src; 49 /* ssize_t len; 50 /* AUXILIARY ROUTINES 51 /* ssize_t netstring_get_length(stream) 52 /* VSTREAM *stream; 53 /* 54 /* VSTRING *netstring_get_data(stream, buf, len) 55 /* VSTREAM *stream; 56 /* VSTRING *buf; 57 /* ssize_t len; 58 /* 59 /* void netstring_get_terminator(stream) 60 /* VSTREAM *stream; 61 /* DESCRIPTION 62 /* This module reads and writes netstrings with error detection: 63 /* timeouts, unexpected end-of-file, or format errors. Netstring 64 /* is a data format designed by Daniel Bernstein. 65 /* 66 /* netstring_setup() arranges for a time limit on the netstring 67 /* read and write operations described below. 68 /* This routine alters the behavior of streams as follows: 69 /* .IP \(bu 70 /* The read/write timeout is set to the specified value. 71 /* .IP \(bu 72 /* The stream is configured to enable exception handling. 73 /* .PP 74 /* netstring_except() raises the specified exception on the 75 /* named stream. See the DIAGNOSTICS section below. 76 /* 77 /* netstring_get() reads a netstring from the specified stream 78 /* and extracts its content. The limit specifies a maximal size. 79 /* Specify zero to disable the size limit. The result is not null 80 /* terminated. The result value is the buf argument. 81 /* 82 /* netstring_put() encapsulates the specified string as a netstring 83 /* and sends the result to the specified stream. 84 /* The stream output buffer is not flushed. 85 /* 86 /* netstring_put_multi() encapsulates the content of multiple strings 87 /* as one netstring and sends the result to the specified stream. The 88 /* argument list must be terminated with a null data pointer. 89 /* The stream output buffer is not flushed. 90 /* 91 /* NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based 92 /* wrapper for the netstring_put() routine. 93 /* 94 /* netstring_fflush() flushes the output buffer of the specified 95 /* stream and handles any errors. 96 /* 97 /* netstring_memcpy() encapsulates the specified data as a netstring 98 /* and copies the result over the specified buffer. The result 99 /* value is the buffer. 100 /* 101 /* netstring_memcat() encapsulates the specified data as a netstring 102 /* and appends the result to the specified buffer. The result 103 /* value is the buffer. 104 /* 105 /* The following routines provide low-level access to a netstring 106 /* stream. 107 /* 108 /* netstring_get_length() reads a length field from the specified 109 /* stream, and absorbs the netstring length field terminator. 110 /* 111 /* netstring_get_data() reads the specified number of bytes from the 112 /* specified stream into the specified buffer, and absorbs the 113 /* netstring terminator. The result value is the buf argument. 114 /* 115 /* netstring_get_terminator() reads the netstring terminator from 116 /* the specified stream. 117 /* DIAGNOSTICS 118 /* .fi 119 /* .ad 120 /* In case of error, a vstream_longjmp() call is performed to the 121 /* context specified with vstream_setjmp(). 122 /* Error codes passed along with vstream_longjmp() are: 123 /* .IP NETSTRING_ERR_EOF 124 /* An I/O error happened, or the peer has disconnected unexpectedly. 125 /* .IP NETSTRING_ERR_TIME 126 /* The time limit specified to netstring_setup() was exceeded. 127 /* .IP NETSTRING_ERR_FORMAT 128 /* The input contains an unexpected character value. 129 /* .IP NETSTRING_ERR_SIZE 130 /* The input is larger than acceptable. 131 /* BUGS 132 /* The timeout deadline affects all I/O on the named stream, not 133 /* just the I/O done on behalf of this module. 134 /* 135 /* The timeout deadline overwrites any previously set up state on 136 /* the named stream. 137 /* 138 /* netstrings are not null terminated, which makes printing them 139 /* a bit awkward. 140 /* LICENSE 141 /* .ad 142 /* .fi 143 /* The Secure Mailer license must be distributed with this software. 144 /* SEE ALSO 145 /* http://cr.yp.to/proto/netstrings.txt, netstring definition 146 /* AUTHOR(S) 147 /* Wietse Venema 148 /* IBM T.J. Watson Research 149 /* P.O. Box 704 150 /* Yorktown Heights, NY 10598, USA 151 /*--*/ 152 153 /* System library. */ 154 155 #include <sys_defs.h> 156 #include <stdarg.h> 157 #include <ctype.h> 158 159 /* Utility library. */ 160 161 #include <msg.h> 162 #include <vstream.h> 163 #include <vstring.h> 164 #include <netstring.h> 165 166 /* Application-specific. */ 167 168 #define STR(x) vstring_str(x) 169 #define LEN(x) VSTRING_LEN(x) 170 171 /* netstring_setup - initialize netstring stream */ 172 173 void netstring_setup(VSTREAM *stream, int timeout) 174 { 175 vstream_control(stream, 176 VSTREAM_CTL_TIMEOUT, timeout, 177 VSTREAM_CTL_EXCEPT, 178 VSTREAM_CTL_END); 179 } 180 181 /* netstring_except - process netstring stream exception */ 182 183 void netstring_except(VSTREAM *stream, int exception) 184 { 185 vstream_longjmp(stream, exception); 186 } 187 188 /* netstring_get_length - read netstring length + terminator */ 189 190 ssize_t netstring_get_length(VSTREAM *stream) 191 { 192 const char *myname = "netstring_get_length"; 193 ssize_t len = 0; 194 int ch; 195 196 for (;;) { 197 switch (ch = VSTREAM_GETC(stream)) { 198 case VSTREAM_EOF: 199 netstring_except(stream, vstream_ftimeout(stream) ? 200 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); 201 case ':': 202 if (msg_verbose > 1) 203 msg_info("%s: read netstring length %ld", myname, (long) len); 204 return (len); 205 default: 206 if (!ISDIGIT(ch)) 207 netstring_except(stream, NETSTRING_ERR_FORMAT); 208 len = len * 10 + ch - '0'; 209 /* vstream_fread() would read zero bytes. Reject input anyway. */ 210 if (len < 0) 211 netstring_except(stream, NETSTRING_ERR_SIZE); 212 break; 213 } 214 } 215 } 216 217 /* netstring_get_data - read netstring payload + terminator */ 218 219 VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, ssize_t len) 220 { 221 const char *myname = "netstring_get_data"; 222 223 /* 224 * Allocate buffer space. 225 */ 226 VSTRING_RESET(buf); 227 VSTRING_SPACE(buf, len); 228 229 /* 230 * Read the payload and absorb the terminator. 231 */ 232 if (vstream_fread(stream, STR(buf), len) != len) 233 netstring_except(stream, vstream_ftimeout(stream) ? 234 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); 235 if (msg_verbose > 1) 236 msg_info("%s: read netstring data %.*s", 237 myname, (int) (len < 30 ? len : 30), STR(buf)); 238 netstring_get_terminator(stream); 239 240 /* 241 * Position the buffer. 242 */ 243 VSTRING_AT_OFFSET(buf, len); 244 return (buf); 245 } 246 247 /* netstring_get_terminator - absorb netstring terminator */ 248 249 void netstring_get_terminator(VSTREAM *stream) 250 { 251 if (VSTREAM_GETC(stream) != ',') 252 netstring_except(stream, NETSTRING_ERR_FORMAT); 253 } 254 255 /* netstring_get - read string from netstring stream */ 256 257 VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, ssize_t limit) 258 { 259 ssize_t len; 260 261 len = netstring_get_length(stream); 262 if (limit && len > limit) 263 netstring_except(stream, NETSTRING_ERR_SIZE); 264 netstring_get_data(stream, buf, len); 265 return (buf); 266 } 267 268 /* netstring_put - send string as netstring */ 269 270 void netstring_put(VSTREAM *stream, const char *data, ssize_t len) 271 { 272 const char *myname = "netstring_put"; 273 274 if (msg_verbose > 1) 275 msg_info("%s: write netstring len %ld data %.*s", 276 myname, (long) len, (int) (len < 30 ? len : 30), data); 277 vstream_fprintf(stream, "%ld:", (long) len); 278 vstream_fwrite(stream, data, len); 279 VSTREAM_PUTC(',', stream); 280 } 281 282 /* netstring_put_multi - send multiple strings as one netstring */ 283 284 void netstring_put_multi(VSTREAM *stream,...) 285 { 286 const char *myname = "netstring_put_multi"; 287 ssize_t total; 288 char *data; 289 ssize_t data_len; 290 va_list ap; 291 292 /* 293 * Figure out the total result size. 294 */ 295 va_start(ap, stream); 296 for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len) 297 if ((data_len = va_arg(ap, ssize_t)) < 0) 298 msg_panic("netstring_put_multi: bad data length %ld", (long) data_len); 299 va_end(ap); 300 301 /* 302 * Debugging support. 303 */ 304 if (msg_verbose > 1) { 305 va_start(ap, stream); 306 data = va_arg(ap, char *); 307 data_len = va_arg(ap, ssize_t); 308 msg_info("%s: write netstring len %ld data %.*s", 309 myname, (long) total, (int) (data_len < 30 ? data_len : 30), data); 310 va_end(ap); 311 } 312 313 /* 314 * Send the length, content and terminator. 315 */ 316 vstream_fprintf(stream, "%ld:", (long) total); 317 va_start(ap, stream); 318 while ((data = va_arg(ap, char *)) != 0) { 319 data_len = va_arg(ap, ssize_t); 320 if (data_len > 0) 321 if (vstream_fwrite(stream, data, data_len) != data_len) 322 netstring_except(stream, vstream_ftimeout(stream) ? 323 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); 324 } 325 va_end(ap); 326 vstream_fwrite(stream, ",", 1); 327 } 328 329 /* netstring_fflush - flush netstring stream */ 330 331 void netstring_fflush(VSTREAM *stream) 332 { 333 if (vstream_fflush(stream) == VSTREAM_EOF) 334 netstring_except(stream, vstream_ftimeout(stream) ? 335 NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); 336 } 337 338 /* netstring_memcpy - copy data as in-memory netstring */ 339 340 VSTRING *netstring_memcpy(VSTRING *buf, const char *src, ssize_t len) 341 { 342 vstring_sprintf(buf, "%ld:", (long) len); 343 vstring_memcat(buf, src, len); 344 VSTRING_ADDCH(buf, ','); 345 return (buf); 346 } 347 348 /* netstring_memcat - append data as in-memory netstring */ 349 350 VSTRING *netstring_memcat(VSTRING *buf, const char *src, ssize_t len) 351 { 352 vstring_sprintf_append(buf, "%ld:", (long) len); 353 vstring_memcat(buf, src, len); 354 VSTRING_ADDCH(buf, ','); 355 return (buf); 356 } 357