xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/netstring.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
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