Postfix3.3.1
record.c
[詳解]
1 /*++
2 /* NAME
3 /* record 3
4 /* SUMMARY
5 /* simple typed record I/O
6 /* SYNOPSIS
7 /* #include <record.h>
8 /*
9 /* int rec_get(stream, buf, maxsize)
10 /* VSTREAM *stream;
11 /* VSTRING *buf;
12 /* ssize_t maxsize;
13 /*
14 /* int rec_get_raw(stream, buf, maxsize, flags)
15 /* VSTREAM *stream;
16 /* VSTRING *buf;
17 /* ssize_t maxsize;
18 /* int flags;
19 /*
20 /* int rec_put(stream, type, data, len)
21 /* VSTREAM *stream;
22 /* int type;
23 /* const char *data;
24 /* ssize_t len;
25 /* AUXILIARY FUNCTIONS
26 /* int rec_put_type(stream, type, offset)
27 /* VSTREAM *stream;
28 /* int type;
29 /* long offset;
30 /*
31 /* int rec_fprintf(stream, type, format, ...)
32 /* VSTREAM *stream;
33 /* int type;
34 /* const char *format;
35 /*
36 /* int rec_fputs(stream, type, str)
37 /* VSTREAM *stream;
38 /* int type;
39 /* const char *str;
40 /*
41 /* int REC_PUT_BUF(stream, type, buf)
42 /* VSTREAM *stream;
43 /* int type;
44 /* VSTRING *buf;
45 /*
46 /* int rec_vfprintf(stream, type, format, ap)
47 /* VSTREAM *stream;
48 /* int type;
49 /* const char *format;
50 /* va_list ap;
51 /*
52 /* int rec_goto(stream, where)
53 /* VSTREAM *stream;
54 /* const char *where;
55 /*
56 /* int rec_pad(stream, type, len)
57 /* VSTREAM *stream;
58 /* int type;
59 /* ssize_t len;
60 /*
61 /* REC_SPACE_NEED(buflen, reclen)
62 /* ssize_t buflen;
63 /* ssize_t reclen;
64 /*
65 /* REC_GET_HIDDEN_TYPE(type)
66 /* int type;
67 /* DESCRIPTION
68 /* This module reads and writes typed variable-length records.
69 /* Each record contains a 1-byte type code (0..255), a length
70 /* (1 or more bytes) and as much data as the length specifies.
71 /*
72 /* rec_get_raw() retrieves a record from the named record stream
73 /* and returns the record type. The \fImaxsize\fR argument is
74 /* zero, or specifies a maximal acceptable record length.
75 /* The result is REC_TYPE_EOF when the end of the file was reached,
76 /* and REC_TYPE_ERROR in case of a bad record. The result buffer is
77 /* null-terminated for convenience. Records may contain embedded
78 /* null characters. The \fIflags\fR argument specifies zero or
79 /* more of the following:
80 /* .IP REC_FLAG_FOLLOW_PTR
81 /* Follow PTR records, instead of exposing them to the application.
82 /* .IP REC_FLAG_SKIP_DTXT
83 /* Skip "deleted text" records, instead of exposing them to
84 /* the application.
85 /* .IP REC_FLAG_SEEK_END
86 /* Seek to the end-of-file upon reading a REC_TYPE_END record.
87 /* .PP
88 /* Specify REC_FLAG_NONE to request no special processing,
89 /* and REC_FLAG_DEFAULT for normal use.
90 /*
91 /* rec_get() is a wrapper around rec_get_raw() that always
92 /* enables the REC_FLAG_FOLLOW_PTR, REC_FLAG_SKIP_DTXT
93 /* and REC_FLAG_SEEK_END features.
94 /*
95 /* REC_GET_HIDDEN_TYPE() is an unsafe macro that returns
96 /* non-zero when the specified record type is "not exposed"
97 /* by rec_get().
98 /*
99 /* rec_put() stores the specified record and returns the record
100 /* type, or REC_TYPE_ERROR in case of problems.
101 /*
102 /* rec_put_type() updates the type field of the record at the
103 /* specified file offset. The result is the new record type,
104 /* or REC_TYPE_ERROR in case of trouble.
105 /*
106 /* rec_fprintf() and rec_vfprintf() format their arguments and
107 /* write the result to the named stream. The result is the same
108 /* as with rec_put().
109 /*
110 /* rec_fputs() writes a record with as contents a copy of the
111 /* specified string. The result is the same as with rec_put().
112 /*
113 /* REC_PUT_BUF() is a wrapper for rec_put() that makes it
114 /* easier to handle VSTRING buffers. It is an unsafe macro
115 /* that evaluates some arguments more than once.
116 /*
117 /* rec_goto() takes the argument of a pointer record and moves
118 /* the file pointer to the specified location. A zero position
119 /* means do nothing. The result is REC_TYPE_ERROR in case of
120 /* failure.
121 /*
122 /* rec_pad() writes a record that occupies the larger of (the
123 /* specified amount) or (an implementation-defined minimum).
124 /*
125 /* REC_SPACE_NEED(buflen, reclen) converts the specified buffer
126 /* length into a record length. This macro modifies its second
127 /* argument.
128 /* DIAGNOSTICS
129 /* Panics: interface violations. Fatal errors: insufficient memory.
130 /* Warnings: corrupted file.
131 /* LICENSE
132 /* .ad
133 /* .fi
134 /* The Secure Mailer license must be distributed with this software.
135 /* AUTHOR(S)
136 /* Wietse Venema
137 /* IBM T.J. Watson Research
138 /* P.O. Box 704
139 /* Yorktown Heights, NY 10598, USA
140 /*
141 /* Wietse Venema
142 /* Google, Inc.
143 /* 111 8th Avenue
144 /* New York, NY 10011, USA
145 /*--*/
146 
147 /* System library. */
148 
149 #include <sys_defs.h>
150 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
151 #include <stdarg.h>
152 #include <unistd.h>
153 #include <string.h>
154 
155 #ifndef NBBY
156 #define NBBY 8 /* XXX should be in sys_defs.h */
157 #endif
158 
159 /* Utility library. */
160 
161 #include <msg.h>
162 #include <mymalloc.h>
163 #include <vstream.h>
164 #include <vstring.h>
165 #include <stringops.h>
166 
167 /* Global library. */
168 
169 #include <off_cvt.h>
170 #include <rec_type.h>
171 #include <record.h>
172 
173 /* rec_put_type - update record type field */
174 
175 int rec_put_type(VSTREAM *stream, int type, off_t offset)
176 {
177  if (type < 0 || type > 255)
178  msg_panic("rec_put_type: bad record type %d", type);
179 
180  if (msg_verbose > 2)
181  msg_info("rec_put_type: %d at %ld", type, (long) offset);
182 
183  if (vstream_fseek(stream, offset, SEEK_SET) < 0
184  || VSTREAM_PUTC(type, stream) != type) {
185  msg_warn("%s: seek or write error", VSTREAM_PATH(stream));
186  return (REC_TYPE_ERROR);
187  } else {
188  return (type);
189  }
190 }
191 
192 /* rec_put - store typed record */
193 
194 int rec_put(VSTREAM *stream, int type, const char *data, ssize_t len)
195 {
196  ssize_t len_rest;
197  int len_byte;
198 
199  if (type < 0 || type > 255)
200  msg_panic("rec_put: bad record type %d", type);
201 
202  if (msg_verbose > 2)
203  msg_info("rec_put: type %c len %ld data %.10s",
204  type, (long) len, data);
205 
206  /*
207  * Write the record type, one byte.
208  */
209  if (VSTREAM_PUTC(type, stream) == VSTREAM_EOF)
210  return (REC_TYPE_ERROR);
211 
212  /*
213  * Write the record data length in 7-bit portions, using the 8th bit to
214  * indicate that there is more. Use as many length bytes as needed.
215  */
216  len_rest = len;
217  do {
218  len_byte = len_rest & 0177;
219  if (len_rest >>= 7U)
220  len_byte |= 0200;
221  if (VSTREAM_PUTC(len_byte, stream) == VSTREAM_EOF) {
222  return (REC_TYPE_ERROR);
223  }
224  } while (len_rest != 0);
225 
226  /*
227  * Write the record data portion. Use as many length bytes as needed.
228  */
229  if (len && vstream_fwrite(stream, data, len) != len)
230  return (REC_TYPE_ERROR);
231  return (type);
232 }
233 
234 /* rec_get_raw - retrieve typed record */
235 
236 int rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags)
237 {
238  const char *myname = "rec_get";
239  int type;
240  ssize_t len;
241  int len_byte;
242  unsigned shift;
243 
244  /*
245  * Sanity check.
246  */
247  if (maxsize < 0)
248  msg_panic("%s: bad record size limit: %ld", myname, (long) maxsize);
249 
250  for (;;) {
251 
252  /*
253  * Extract the record type.
254  */
255  if ((type = VSTREAM_GETC(stream)) == VSTREAM_EOF)
256  return (REC_TYPE_EOF);
257 
258  /*
259  * Find out the record data length. Return an error result when the
260  * record data length is malformed or when it exceeds the acceptable
261  * limit.
262  */
263  for (len = 0, shift = 0; /* void */ ; shift += 7) {
264  if (shift >= (int) (NBBY * sizeof(int))) {
265  msg_warn("%s: too many length bits, record type %d",
266  VSTREAM_PATH(stream), type);
267  return (REC_TYPE_ERROR);
268  }
269  if ((len_byte = VSTREAM_GETC(stream)) == VSTREAM_EOF) {
270  msg_warn("%s: unexpected EOF reading length, record type %d",
271  VSTREAM_PATH(stream), type);
272  return (REC_TYPE_ERROR);
273  }
274  len |= (len_byte & 0177) << shift;
275  if ((len_byte & 0200) == 0)
276  break;
277  }
278  if (len < 0 || (maxsize > 0 && len > maxsize)) {
279  msg_warn("%s: illegal length %ld, record type %d",
280  VSTREAM_PATH(stream), (long) len, type);
281  while (len-- > 0 && VSTREAM_GETC(stream) != VSTREAM_EOF)
282  /* void */ ;
283  return (REC_TYPE_ERROR);
284  }
285 
286  /*
287  * Reserve buffer space for the result, and read the record data into
288  * the buffer.
289  */
290  VSTRING_RESET(buf);
291  VSTRING_SPACE(buf, len);
292  if (vstream_fread(stream, vstring_str(buf), len) != len) {
293  msg_warn("%s: unexpected EOF in data, record type %d length %ld",
294  VSTREAM_PATH(stream), type, (long) len);
295  return (REC_TYPE_ERROR);
296  }
297  VSTRING_AT_OFFSET(buf, len);
298  VSTRING_TERMINATE(buf);
299  if (msg_verbose > 2)
300  msg_info("%s: type %c len %ld data %.10s", myname,
301  type, (long) len, vstring_str(buf));
302 
303  /*
304  * Transparency options.
305  */
306  if (flags == 0)
307  break;
308  if (type == REC_TYPE_PTR && (flags & REC_FLAG_FOLLOW_PTR) != 0
309  && (type = rec_goto(stream, vstring_str(buf))) != REC_TYPE_ERROR)
310  continue;
311  if (type == REC_TYPE_DTXT && (flags & REC_FLAG_SKIP_DTXT) != 0)
312  continue;
313  if (type == REC_TYPE_END && (flags & REC_FLAG_SEEK_END) != 0
314  && vstream_fseek(stream, (off_t) 0, SEEK_END) < 0) {
315  msg_warn("%s: seek error after reading END record: %m",
316  VSTREAM_PATH(stream));
317  return (REC_TYPE_ERROR);
318  }
319  break;
320  }
321  return (type);
322 }
323 
324 /* rec_goto - follow PTR record */
325 
326 int rec_goto(VSTREAM *stream, const char *buf)
327 {
328  off_t offset;
329  static const char *saved_path;
330  static off_t saved_offset;
331  static int reverse_count;
332 
333  /*
334  * Crude workaround for queue file loops. VSTREAMs currently have no
335  * option to attach application-specific data, so we use global state and
336  * simple logic to detect if an application switches streams. We trigger
337  * on reverse jumps only. There's one reverse jump for every inserted
338  * header, but only one reverse jump for all appended recipients. No-one
339  * is likely to insert 10000 message headers, but someone might append
340  * 10000 recipients.
341  */
342 #define STREQ(x,y) ((x) == (y) && strcmp((x), (y)) == 0)
343 #define REVERSE_JUMP_LIMIT 10000
344 
345  if (!STREQ(saved_path, VSTREAM_PATH(stream))) {
346  saved_path = VSTREAM_PATH(stream);
347  reverse_count = 0;
348  saved_offset = 0;
349  }
350  while (ISSPACE(*buf))
351  buf++;
352  if ((offset = off_cvt_string(buf)) < 0) {
353  msg_warn("%s: malformed pointer record value: %s",
354  VSTREAM_PATH(stream), buf);
355  return (REC_TYPE_ERROR);
356  } else if (offset == 0) {
357  /* Dummy record. */
358  return (0);
359  } else if (offset <= saved_offset && ++reverse_count > REVERSE_JUMP_LIMIT) {
360  msg_warn("%s: too many reverse jump records", VSTREAM_PATH(stream));
361  return (REC_TYPE_ERROR);
362  } else if (vstream_fseek(stream, offset, SEEK_SET) < 0) {
363  msg_warn("%s: seek error after pointer record: %m",
364  VSTREAM_PATH(stream));
365  return (REC_TYPE_ERROR);
366  } else {
367  saved_offset = offset;
368  return (0);
369  }
370 }
371 
372 /* rec_vfprintf - write formatted string to record */
373 
374 int rec_vfprintf(VSTREAM *stream, int type, const char *format, va_list ap)
375 {
376  static VSTRING *vp;
377 
378  if (vp == 0)
379  vp = vstring_alloc(100);
380 
381  /*
382  * Writing a formatted string involves an extra copy, because we must
383  * know the record length before we can write it.
384  */
385  vstring_vsprintf(vp, format, ap);
386  return (REC_PUT_BUF(stream, type, vp));
387 }
388 
389 /* rec_fprintf - write formatted string to record */
390 
391 int rec_fprintf(VSTREAM *stream, int type, const char *format,...)
392 {
393  int result;
394  va_list ap;
395 
396  va_start(ap, format);
397  result = rec_vfprintf(stream, type, format, ap);
398  va_end(ap);
399  return (result);
400 }
401 
402 /* rec_fputs - write string to record */
403 
404 int rec_fputs(VSTREAM *stream, int type, const char *str)
405 {
406  return (rec_put(stream, type, str, str ? strlen(str) : 0));
407 }
408 
409 /* rec_pad - write padding record */
410 
411 int rec_pad(VSTREAM *stream, int type, ssize_t len)
412 {
413  int width = len - 2; /* type + length */
414 
415  return (rec_fprintf(stream, type, "%*s",
416  width < 1 ? 1 : width, "0"));
417 }
int msg_verbose
Definition: msg.c:177
#define VSTREAM_EOF
Definition: vstream.h:110
int rec_put_type(VSTREAM *stream, int type, off_t offset)
Definition: record.c:175
int rec_vfprintf(VSTREAM *stream, int type, const char *format, va_list ap)
Definition: record.c:374
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define REC_FLAG_SKIP_DTXT
Definition: record.h:47
#define VSTREAM_GETC(vp)
Definition: vstream.h:108
#define REC_FLAG_FOLLOW_PTR
Definition: record.h:46
int rec_goto(VSTREAM *stream, const char *buf)
Definition: record.c:326
off_t off_cvt_string(const char *str)
Definition: off_cvt.c:71
#define STREQ(x, y)
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define REC_TYPE_END
Definition: rec_type.h:77
#define REC_TYPE_EOF
Definition: rec_type.h:23
#define VSTRING_TERMINATE(vp)
Definition: vstring.h:74
#define REC_PUT_BUF(v, t, b)
Definition: record.h:43
int rec_get_raw(VSTREAM *stream, VSTRING *buf, ssize_t maxsize, int flags)
Definition: record.c:236
VSTRING * vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
Definition: vstring.c:614
#define REC_FLAG_SEEK_END
Definition: record.h:48
#define REVERSE_JUMP_LIMIT
int rec_pad(VSTREAM *stream, int type, ssize_t len)
Definition: record.c:411
#define VSTRING_RESET(vp)
Definition: vstring.h:77
int rec_fputs(VSTREAM *stream, int type, const char *str)
Definition: record.c:404
#define REC_TYPE_PTR
Definition: rec_type.h:67
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int rec_put(VSTREAM *stream, int type, const char *data, ssize_t len)
Definition: record.c:194
#define NBBY
Definition: record.c:156
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
#define vstream_fread(v, b, n)
Definition: vstream.h:104
#define vstream_fwrite(v, b, n)
Definition: vstream.h:105
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
#define VSTRING_AT_OFFSET(vp, offset)
Definition: vstring.h:92
#define REC_TYPE_ERROR
Definition: rec_type.h:24
#define ISSPACE(c)
Definition: sys_defs.h:1753
#define VSTREAM_PUTC(ch, vp)
Definition: vstream.h:107
int rec_fprintf(VSTREAM *stream, int type, const char *format,...)
Definition: record.c:391
#define REC_TYPE_DTXT
Definition: rec_type.h:60
void msg_info(const char *fmt,...)
Definition: msg.c:199