Postfix3.3.1
netstring.c
[詳解]
1 /*++
2 /* NAME
3 /* netstring 3
4 /* SUMMARY
5 /* netstring stream I/O support
6 /* SYNOPSIS
7 /* #include <netstring.h>
8 /*
9 /* void netstring_setup(stream, timeout)
10 /* VSTREAM *stream;
11 /* int timeout;
12 /*
13 /* void netstring_except(stream, exception)
14 /* VSTREAM *stream;
15 /* int exception;
16 /*
17 /* const char *netstring_strerror(err)
18 /* int err;
19 /*
20 /* VSTRING *netstring_get(stream, buf, limit)
21 /* VSTREAM *stream;
22 /* VSTRING *buf;
23 /* ssize_t limit;
24 /*
25 /* void netstring_put(stream, data, len)
26 /* VSTREAM *stream;
27 /* const char *data;
28 /* ssize_t len;
29 /*
30 /* void netstring_put_multi(stream, data, len, data, len, ..., 0)
31 /* VSTREAM *stream;
32 /* const char *data;
33 /* ssize_t len;
34 /*
35 /* void NETSTRING_PUT_BUF(stream, buf)
36 /* VSTREAM *stream;
37 /* VSTRING *buf;
38 /*
39 /* void netstring_fflush(stream)
40 /* VSTREAM *stream;
41 /*
42 /* VSTRING *netstring_memcpy(buf, data, len)
43 /* VSTRING *buf;
44 /* const char *data;
45 /* ssize_t len;
46 /*
47 /* VSTRING *netstring_memcat(buf, data, len)
48 /* VSTRING *buf;
49 /* const char *src;
50 /* ssize_t len;
51 /* AUXILIARY ROUTINES
52 /* ssize_t netstring_get_length(stream)
53 /* VSTREAM *stream;
54 /*
55 /* VSTRING *netstring_get_data(stream, buf, len)
56 /* VSTREAM *stream;
57 /* VSTRING *buf;
58 /* ssize_t len;
59 /*
60 /* void netstring_get_terminator(stream)
61 /* VSTREAM *stream;
62 /* DESCRIPTION
63 /* This module reads and writes netstrings with error detection:
64 /* timeouts, unexpected end-of-file, or format errors. Netstring
65 /* is a data format designed by Daniel Bernstein.
66 /*
67 /* netstring_setup() arranges for a time limit on the netstring
68 /* read and write operations described below.
69 /* This routine alters the behavior of streams as follows:
70 /* .IP \(bu
71 /* The read/write timeout is set to the specified value.
72 /* .IP \(bu
73 /* The stream is configured to enable exception handling.
74 /* .PP
75 /* netstring_except() raises the specified exception on the
76 /* named stream. See the DIAGNOSTICS section below.
77 /*
78 /* netstring_strerror() converts an exception number to string.
79 /*
80 /* netstring_get() reads a netstring from the specified stream
81 /* and extracts its content. The limit specifies a maximal size.
82 /* Specify zero to disable the size limit. The result is not null
83 /* terminated. The result value is the buf argument.
84 /*
85 /* netstring_put() encapsulates the specified string as a netstring
86 /* and sends the result to the specified stream.
87 /* The stream output buffer is not flushed.
88 /*
89 /* netstring_put_multi() encapsulates the content of multiple strings
90 /* as one netstring and sends the result to the specified stream. The
91 /* argument list must be terminated with a null data pointer.
92 /* The stream output buffer is not flushed.
93 /*
94 /* NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based
95 /* wrapper for the netstring_put() routine.
96 /*
97 /* netstring_fflush() flushes the output buffer of the specified
98 /* stream and handles any errors.
99 /*
100 /* netstring_memcpy() encapsulates the specified data as a netstring
101 /* and copies the result over the specified buffer. The result
102 /* value is the buffer.
103 /*
104 /* netstring_memcat() encapsulates the specified data as a netstring
105 /* and appends the result to the specified buffer. The result
106 /* value is the buffer.
107 /*
108 /* The following routines provide low-level access to a netstring
109 /* stream.
110 /*
111 /* netstring_get_length() reads a length field from the specified
112 /* stream, and absorbs the netstring length field terminator.
113 /*
114 /* netstring_get_data() reads the specified number of bytes from the
115 /* specified stream into the specified buffer, and absorbs the
116 /* netstring terminator. The result value is the buf argument.
117 /*
118 /* netstring_get_terminator() reads the netstring terminator from
119 /* the specified stream.
120 /* DIAGNOSTICS
121 /* .fi
122 /* .ad
123 /* In case of error, a vstream_longjmp() call is performed to the
124 /* caller-provided context specified with vstream_setjmp().
125 /* Error codes passed along with vstream_longjmp() are:
126 /* .IP NETSTRING_ERR_EOF
127 /* An I/O error happened, or the peer has disconnected unexpectedly.
128 /* .IP NETSTRING_ERR_TIME
129 /* The time limit specified to netstring_setup() was exceeded.
130 /* .IP NETSTRING_ERR_FORMAT
131 /* The input contains an unexpected character value.
132 /* .IP NETSTRING_ERR_SIZE
133 /* The input is larger than acceptable.
134 /* BUGS
135 /* The timeout deadline affects all I/O on the named stream, not
136 /* just the I/O done on behalf of this module.
137 /*
138 /* The timeout deadline overwrites any previously set up state on
139 /* the named stream.
140 /*
141 /* netstrings are not null terminated, which makes printing them
142 /* a bit awkward.
143 /* LICENSE
144 /* .ad
145 /* .fi
146 /* The Secure Mailer license must be distributed with this software.
147 /* SEE ALSO
148 /* http://cr.yp.to/proto/netstrings.txt, netstring definition
149 /* AUTHOR(S)
150 /* Wietse Venema
151 /* IBM T.J. Watson Research
152 /* P.O. Box 704
153 /* Yorktown Heights, NY 10598, USA
154 /*
155 /* Wietse Venema
156 /* Google, Inc.
157 /* 111 8th Avenue
158 /* New York, NY 10011, USA
159 /*--*/
160 
161 /* System library. */
162 
163 #include <sys_defs.h>
164 #include <stdarg.h>
165 #include <ctype.h>
166 
167 /* Utility library. */
168 
169 #include <msg.h>
170 #include <vstream.h>
171 #include <vstring.h>
172 #include <compat_va_copy.h>
173 #include <netstring.h>
174 
175 /* Application-specific. */
176 
177 #define STR(x) vstring_str(x)
178 #define LEN(x) VSTRING_LEN(x)
179 
180 /* netstring_setup - initialize netstring stream */
181 
182 void netstring_setup(VSTREAM *stream, int timeout)
183 {
184  vstream_control(stream,
185  CA_VSTREAM_CTL_TIMEOUT(timeout),
188 }
189 
190 /* netstring_except - process netstring stream exception */
191 
192 void netstring_except(VSTREAM *stream, int exception)
193 {
194  vstream_longjmp(stream, exception);
195 }
196 
197 /* netstring_get_length - read netstring length + terminator */
198 
200 {
201  const char *myname = "netstring_get_length";
202  ssize_t len = 0;
203  int ch;
204  int digit;
205 
206  for (;;) {
207  switch (ch = VSTREAM_GETC(stream)) {
208  case VSTREAM_EOF:
209  netstring_except(stream, vstream_ftimeout(stream) ?
211  case ':':
212  if (msg_verbose > 1)
213  msg_info("%s: read netstring length %ld", myname, (long) len);
214  return (len);
215  default:
216  if (!ISDIGIT(ch))
218  digit = ch - '0';
219  if (len > SSIZE_T_MAX / 10
220  || (len *= 10) > SSIZE_T_MAX - digit)
222  len += digit;
223  break;
224  }
225  }
226 }
227 
228 /* netstring_get_data - read netstring payload + terminator */
229 
230 VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, ssize_t len)
231 {
232  const char *myname = "netstring_get_data";
233 
234  /*
235  * Allocate buffer space.
236  */
237  VSTRING_RESET(buf);
238  VSTRING_SPACE(buf, len);
239 
240  /*
241  * Read the payload and absorb the terminator.
242  */
243  if (vstream_fread(stream, STR(buf), len) != len)
244  netstring_except(stream, vstream_ftimeout(stream) ?
246  if (msg_verbose > 1)
247  msg_info("%s: read netstring data %.*s",
248  myname, (int) (len < 30 ? len : 30), STR(buf));
249  netstring_get_terminator(stream);
250 
251  /*
252  * Position the buffer.
253  */
254  VSTRING_AT_OFFSET(buf, len);
255  return (buf);
256 }
257 
258 /* netstring_get_terminator - absorb netstring terminator */
259 
261 {
262  if (VSTREAM_GETC(stream) != ',')
264 }
265 
266 /* netstring_get - read string from netstring stream */
267 
268 VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, ssize_t limit)
269 {
270  ssize_t len;
271 
272  len = netstring_get_length(stream);
273  if (limit && len > limit)
275  netstring_get_data(stream, buf, len);
276  return (buf);
277 }
278 
279 /* netstring_put - send string as netstring */
280 
281 void netstring_put(VSTREAM *stream, const char *data, ssize_t len)
282 {
283  const char *myname = "netstring_put";
284 
285  if (msg_verbose > 1)
286  msg_info("%s: write netstring len %ld data %.*s",
287  myname, (long) len, (int) (len < 30 ? len : 30), data);
288  vstream_fprintf(stream, "%ld:", (long) len);
289  vstream_fwrite(stream, data, len);
290  VSTREAM_PUTC(',', stream);
291 }
292 
293 /* netstring_put_multi - send multiple strings as one netstring */
294 
295 void netstring_put_multi(VSTREAM *stream,...)
296 {
297  const char *myname = "netstring_put_multi";
298  ssize_t total;
299  char *data;
300  ssize_t data_len;
301  va_list ap;
302  va_list ap2;
303 
304  /*
305  * Initialize argument lists.
306  */
307  va_start(ap, stream);
308  VA_COPY(ap2, ap);
309 
310  /*
311  * Figure out the total result size.
312  */
313  for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len)
314  if ((data_len = va_arg(ap, ssize_t)) < 0)
315  msg_panic("%s: bad data length %ld", myname, (long) data_len);
316  va_end(ap);
317  if (total < 0)
318  msg_panic("%s: bad total length %ld", myname, (long) total);
319  if (msg_verbose > 1)
320  msg_info("%s: write total length %ld", myname, (long) total);
321 
322  /*
323  * Send the length, content and terminator.
324  */
325  vstream_fprintf(stream, "%ld:", (long) total);
326  while ((data = va_arg(ap2, char *)) != 0) {
327  data_len = va_arg(ap2, ssize_t);
328  if (msg_verbose > 1)
329  msg_info("%s: write netstring len %ld data %.*s",
330  myname, (long) data_len,
331  (int) (data_len < 30 ? data_len : 30), data);
332  if (vstream_fwrite(stream, data, data_len) != data_len)
333  netstring_except(stream, vstream_ftimeout(stream) ?
335  }
336  va_end(ap2);
337  vstream_fwrite(stream, ",", 1);
338 }
339 
340 /* netstring_fflush - flush netstring stream */
341 
343 {
344  if (vstream_fflush(stream) == VSTREAM_EOF)
345  netstring_except(stream, vstream_ftimeout(stream) ?
347 }
348 
349 /* netstring_memcpy - copy data as in-memory netstring */
350 
351 VSTRING *netstring_memcpy(VSTRING *buf, const char *src, ssize_t len)
352 {
353  vstring_sprintf(buf, "%ld:", (long) len);
354  vstring_memcat(buf, src, len);
355  VSTRING_ADDCH(buf, ',');
356  return (buf);
357 }
358 
359 /* netstring_memcat - append data as in-memory netstring */
360 
361 VSTRING *netstring_memcat(VSTRING *buf, const char *src, ssize_t len)
362 {
363  vstring_sprintf_append(buf, "%ld:", (long) len);
364  vstring_memcat(buf, src, len);
365  VSTRING_ADDCH(buf, ',');
366  return (buf);
367 }
368 
369 /* netstring_strerror - convert error number to string */
370 
371 const char *netstring_strerror(int err)
372 {
373  switch (err) {
374  case NETSTRING_ERR_EOF:
375  return ("unexpected disconnect");
376  case NETSTRING_ERR_TIME:
377  return ("time limit exceeded");
379  return ("input format error");
380  case NETSTRING_ERR_SIZE:
381  return ("input exceeds size limit");
382  default:
383  return ("unknown netstring error");
384  }
385 }
386 
387  /*
388  * Proof-of-concept netstring encoder/decoder.
389  *
390  * Usage: netstring command...
391  *
392  * Run the command as a child process. Then, convert between plain strings on
393  * our own stdin/stdout, and netstrings on the child program's stdin/stdout.
394  *
395  * Example (socketmap test server): netstring nc -l 9999
396  */
397 #ifdef TEST
398 #include <unistd.h>
399 #include <stdlib.h>
400 #include <events.h>
401 
402 static VSTRING *stdin_read_buf; /* stdin line buffer */
403 static VSTRING *child_read_buf; /* child read buffer */
404 static VSTREAM *child_stream; /* child stream (full-duplex) */
405 
406 /* stdin_read_event - line-oriented event handler */
407 
408 static void stdin_read_event(int event, void *context)
409 {
410  int ch;
411 
412  /*
413  * Send a netstring to the child when we have accumulated an entire line
414  * of input.
415  *
416  * Note: the first VSTREAM_GETCHAR() call implicitly fills the VSTREAM
417  * buffer. We must drain the entire VSTREAM buffer before requesting the
418  * next read(2) event.
419  */
420  do {
421  ch = VSTREAM_GETCHAR();
422  switch (ch) {
423  default:
424  VSTRING_ADDCH(stdin_read_buf, ch);
425  break;
426  case '\n':
427  NETSTRING_PUT_BUF(child_stream, stdin_read_buf);
428  vstream_fflush(child_stream);
429  VSTRING_RESET(stdin_read_buf);
430  break;
431  case VSTREAM_EOF:
432  /* Better: wait for child to terminate. */
433  sleep(1);
434  exit(0);
435  }
436  } while (vstream_peek(VSTREAM_IN) > 0);
437 }
438 
439 /* child_read_event - netstring-oriented event handler */
440 
441 static void child_read_event(int event, void *context)
442 {
443 
444  /*
445  * Read an entire netstring from the child and send the result to stdout.
446  *
447  * This is a simplistic implementation that assumes a server will not
448  * trickle its data.
449  *
450  * Note: the first netstring_get() call implicitly fills the VSTREAM buffer.
451  * We must drain the entire VSTREAM buffer before requesting the next
452  * read(2) event.
453  */
454  do {
455  netstring_get(child_stream, child_read_buf, 10000);
456  vstream_fwrite(VSTREAM_OUT, STR(child_read_buf), LEN(child_read_buf));
457  VSTREAM_PUTC('\n', VSTREAM_OUT);
459  } while (vstream_peek(child_stream) > 0);
460 }
461 
462 int main(int argc, char **argv)
463 {
464  int err;
465 
466  /*
467  * Sanity check.
468  */
469  if (argv[1] == 0)
470  msg_fatal("usage: %s command...", argv[0]);
471 
472  /*
473  * Run the specified command as a child process with stdin and stdout
474  * connected to us.
475  */
476  child_stream = vstream_popen(O_RDWR, CA_VSTREAM_POPEN_ARGV(argv + 1),
479  netstring_setup(child_stream, 10);
480 
481  /*
482  * Buffer plumbing.
483  */
484  stdin_read_buf = vstring_alloc(100);
485  child_read_buf = vstring_alloc(100);
486 
487  /*
488  * Monitor both the child's stdout stream and our own stdin stream. If
489  * there is activity on the child stdout stream, read an entire netstring
490  * or EOF. If there is activity on stdin, send a netstring to the child
491  * when we have read an entire line, or terminate in case of EOF.
492  */
493  event_enable_read(vstream_fileno(VSTREAM_IN), stdin_read_event, (void *) 0);
494  event_enable_read(vstream_fileno(child_stream), child_read_event,
495  (void *) 0);
496 
497  if ((err = vstream_setjmp(child_stream)) == 0) {
498  for (;;)
499  event_loop(-1);
500  } else {
501  msg_fatal("%s: %s", argv[1], netstring_strerror(err));
502  }
503 }
504 
505 #endif
int msg_verbose
Definition: msg.c:177
void event_enable_read(int fd, EVENT_NOTIFY_RDWR_FN callback, void *context)
Definition: events.c:729
#define VSTREAM_EOF
Definition: vstream.h:110
void netstring_except(VSTREAM *stream, int exception)
Definition: netstring.c:192
void netstring_fflush(VSTREAM *stream)
Definition: netstring.c:342
#define CA_VSTREAM_CTL_TIMEOUT(v)
Definition: vstream.h:163
#define NETSTRING_ERR_FORMAT
Definition: netstring.h:25
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
VSTRING * netstring_get(VSTREAM *stream, VSTRING *buf, ssize_t limit)
Definition: netstring.c:268
#define VSTREAM_OUT
Definition: vstream.h:67
ssize_t netstring_get_length(VSTREAM *stream)
Definition: netstring.c:199
int main(int argc, char **argv)
Definition: anvil.c:1010
#define VSTREAM_GETC(vp)
Definition: vstream.h:108
VSTRING * netstring_memcpy(VSTRING *buf, const char *src, ssize_t len)
Definition: netstring.c:351
#define vstream_longjmp(stream, val)
Definition: vstream.h:249
#define CA_VSTREAM_POPEN_ARGV(v)
Definition: vstream.h:204
void netstring_setup(VSTREAM *stream, int timeout)
Definition: netstring.c:182
#define CA_VSTREAM_CTL_DOUBLE
Definition: vstream.h:159
#define VSTREAM_IN
Definition: vstream.h:66
#define vstream_setjmp(stream)
Definition: vstream.h:248
#define VA_COPY(dest, src)
#define NETSTRING_ERR_SIZE
Definition: netstring.h:26
VSTRING * netstring_get_data(VSTREAM *stream, VSTRING *buf, ssize_t len)
Definition: netstring.c:230
#define VSTRING_ADDCH(vp, ch)
Definition: vstring.h:81
VSTRING * vstring_sprintf_append(VSTRING *vp, const char *format,...)
Definition: vstring.c:624
#define ISDIGIT(c)
Definition: sys_defs.h:1748
VSTREAM * vstream_fprintf(VSTREAM *stream, const char *fmt,...)
Definition: vstream.c:1348
VSTRING * netstring_memcat(VSTRING *buf, const char *src, ssize_t len)
Definition: netstring.c:361
#define vstream_ftimeout(vp)
Definition: vstream.h:124
#define VSTREAM_GETCHAR()
Definition: vstream.h:113
void event_loop(int delay)
Definition: events.c:998
#define VSTRING_RESET(vp)
Definition: vstring.h:77
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define CA_VSTREAM_POPEN_END
Definition: vstream.h:202
const char * netstring_strerror(int err)
Definition: netstring.c:371
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
void netstring_put_multi(VSTREAM *stream,...)
Definition: netstring.c:295
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define vstream_fread(v, b, n)
Definition: vstream.h:104
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
VSTRING * vstring_memcat(VSTRING *vp, const char *src, ssize_t len)
Definition: vstring.c:495
#define vstream_fwrite(v, b, n)
Definition: vstream.h:105
#define vstream_peek(vp)
Definition: vstream.h:232
void netstring_put(VSTREAM *stream, const char *data, ssize_t len)
Definition: netstring.c:281
#define NETSTRING_ERR_EOF
Definition: netstring.h:23
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
#define NETSTRING_ERR_TIME
Definition: netstring.h:24
#define SSIZE_T_MAX
Definition: sys_defs.h:1687
#define CA_VSTREAM_CTL_EXCEPT
Definition: vstream.h:164
#define LEN(x)
Definition: netstring.c:178
#define vstream_fileno(vp)
Definition: vstream.h:115
#define VSTRING_AT_OFFSET(vp, offset)
Definition: vstring.h:92
#define CA_VSTREAM_CTL_END
Definition: vstream.h:155
#define STR(x)
Definition: netstring.c:177
#define VSTREAM_PUTC(ch, vp)
Definition: vstream.h:107
void vstream_control(VSTREAM *stream, int name,...)
Definition: vstream.c:1372
VSTREAM VSTREAM const char VSTREAM * vstream_popen(int,...)
void netstring_get_terminator(VSTREAM *stream)
Definition: netstring.c:260
#define NETSTRING_PUT_BUF(str, buf)
Definition: netstring.h:41
void msg_info(const char *fmt,...)
Definition: msg.c:199