5
/* recover attributes from byte stream
9
/* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END)
15
/* int attr_vscan0(fp, flags, ap)
20
/* attr_scan0() takes zero or more (name, value) request attributes
21
/* and recovers the attribute values from the byte stream that was
22
/* possibly generated by attr_print0().
24
/* attr_vscan0() provides an alternative interface that is convenient
25
/* for calling from within a variadic function.
27
/* The input stream is formatted as follows, where (item)* stands
28
/* for zero or more instances of the specified item, and where
29
/* (item1 | item2) stands for choice:
32
/* attr-list :== simple-attr* null
34
/* simple-attr :== attr-name null attr-value null
36
/* attr-name :== any string not containing null
38
/* attr-value :== any string not containing null
40
/* null :== the ASCII null character
43
/* All attribute names and attribute values are sent as null terminated
44
/* strings. Each string must be no longer than 4*var_line_limit
45
/* characters including the terminator.
46
/* These formatting rules favor implementations in C.
48
/* Normally, attributes must be received in the sequence as specified with
49
/* the attr_scan0() argument list. The input stream may contain additional
50
/* attributes at any point in the input stream, including additional
51
/* instances of requested attributes.
53
/* Additional input attributes or input attribute instances are silently
54
/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified
55
/* (see below). This allows for some flexibility in the evolution of
56
/* protocols while still providing the option of being strict where
61
/* Stream to recover the input attributes from.
63
/* The bit-wise OR of zero or more of the following.
65
/* .IP ATTR_FLAG_MISSING
66
/* Log a warning when the input attribute list terminates before all
67
/* requested attributes are recovered. It is always an error when the
68
/* input stream ends without the newline attribute list terminator.
69
/* .IP ATTR_FLAG_EXTRA
70
/* Log a warning and stop attribute recovery when the input stream
71
/* contains an attribute that was not requested. This includes the
72
/* case of additional instances of a requested attribute.
74
/* After recovering the requested attributes, leave the input stream
75
/* in a state that is usable for more attr_scan0() operations from the
76
/* same input attribute list.
77
/* By default, attr_scan0() skips forward past the input attribute list
79
/* .IP ATTR_FLAG_STRICT
80
/* For convenience, this value combines both ATTR_FLAG_MISSING and
83
/* For convenience, this value requests none of the above.
86
/* The type argument determines the arguments that follow.
88
/* .IP "ATTR_TYPE_NUM (char *, int *)"
89
/* This argument is followed by an attribute name and an integer pointer.
90
/* .IP "ATTR_TYPE_LONG (char *, long *)"
91
/* This argument is followed by an attribute name and a long pointer.
92
/* .IP "ATTR_TYPE_STR (char *, VSTRING *)"
93
/* This argument is followed by an attribute name and a VSTRING pointer.
94
/* .IP "ATTR_TYPE_HASH (HTABLE *)"
95
/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)"
96
/* All further input attributes are processed as string attributes.
97
/* No specific attribute sequence is enforced.
98
/* All attributes up to the attribute list terminator are read,
99
/* but only the first instance of each attribute is stored.
100
/* There can be no more than 1024 attributes in a hash table.
102
/* The attribute string values are stored in the hash table under
103
/* keys equal to the attribute name (obtained from the input stream).
104
/* Values from the input stream are added to the hash table. Existing
105
/* hash table entries are not replaced.
107
/* N.B. This construct must be followed by an ATTR_TYPE_END argument.
109
/* This argument terminates the requested attribute list.
112
/* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary
113
/* names from possibly untrusted sources.
114
/* This is unsafe, unless the resulting table is queried only with
115
/* known to be good attribute names.
117
/* attr_scan0() and attr_vscan0() return -1 when malformed input is
118
/* detected (string too long, incomplete line, missing end marker).
119
/* Otherwise, the result value is the number of attributes that were
120
/* successfully recovered from the input stream (a hash table counts
121
/* as the number of entries stored into the table).
123
/* Panic: interface violation. All system call errors are fatal.
125
/* attr_print0(3) send attributes over byte stream.
129
/* The Secure Mailer license must be distributed with this software.
132
/* IBM T.J. Watson Research
134
/* Yorktown Heights, NY 10598, USA
137
/* System library. */
139
#include <sys_defs.h>
144
/* Utility library. */
147
#include <mymalloc.h>
150
#include <vstring_vstream.h>
154
/* Application specific. */
156
#define STR(x) vstring_str(x)
157
#define LEN(x) VSTRING_LEN(x)
159
/* attr_scan0_string - pull a string from the input stream */
161
static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context)
165
if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) {
166
msg_warn("%s on %s while reading %s",
167
vstream_ftimeout(fp) ? "timeout" : "premature end-of-input",
168
VSTREAM_PATH(fp), context);
172
msg_warn("unexpected end-of-input from %s while reading %s",
173
VSTREAM_PATH(fp), context);
177
msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)");
181
/* attr_scan0_number - pull a number from the input stream */
183
static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf,
189
if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
191
if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) {
192
msg_warn("malformed numerical data from %s while reading %s: %.100s",
193
VSTREAM_PATH(fp), context, STR(str_buf));
199
/* attr_scan0_long_number - pull a number from the input stream */
201
static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr,
208
if ((ch = attr_scan0_string(fp, str_buf, context)) < 0)
210
if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) {
211
msg_warn("malformed numerical data from %s while reading %s: %.100s",
212
VSTREAM_PATH(fp), context, STR(str_buf));
218
/* attr_vscan0 - receive attribute list from stream */
220
int attr_vscan0(VSTREAM *fp, int flags, va_list ap)
222
const char *myname = "attr_scan0";
223
static VSTRING *str_buf = 0;
224
static VSTRING *name_buf = 0;
225
int wanted_type = -1;
227
unsigned int *number;
228
unsigned long *long_number;
237
if (flags & ~ATTR_FLAG_ALL)
238
msg_panic("%s: bad flags: 0x%x", myname, flags);
244
str_buf = vstring_alloc(10);
245
name_buf = vstring_alloc(10);
249
* Iterate over all (type, name, value) triples.
251
for (conversions = 0; /* void */ ; conversions++) {
254
* Determine the next attribute type and attribute name on the
255
* caller's wish list.
257
* If we're reading into a hash table, we already know that the
258
* attribute value is string-valued, and we get the attribute name
259
* from the input stream instead. This is secure only when the
260
* resulting table is queried with known to be good attribute names.
262
if (wanted_type != ATTR_TYPE_HASH) {
263
wanted_type = va_arg(ap, int);
264
if (wanted_type == ATTR_TYPE_END) {
265
if ((flags & ATTR_FLAG_MORE) != 0)
266
return (conversions);
267
wanted_name = "(list terminator)";
268
} else if (wanted_type == ATTR_TYPE_HASH) {
269
wanted_name = "(any attribute name or list terminator)";
270
hash_table = va_arg(ap, HTABLE *);
271
if (va_arg(ap, int) !=ATTR_TYPE_END)
272
msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END",
275
wanted_name = va_arg(ap, char *);
280
* Locate the next attribute of interest in the input stream.
285
* Get the name of the next attribute. Hitting EOF is always bad.
286
* Hitting the end-of-input early is OK if the caller is prepared
287
* to deal with missing inputs.
290
msg_info("%s: wanted attribute: %s",
291
VSTREAM_PATH(fp), wanted_name);
292
if ((ch = attr_scan0_string(fp, name_buf,
293
"input attribute name")) == VSTREAM_EOF)
295
if (LEN(name_buf) == 0) {
296
if (wanted_type == ATTR_TYPE_END
297
|| wanted_type == ATTR_TYPE_HASH)
298
return (conversions);
299
if ((flags & ATTR_FLAG_MISSING) != 0)
300
msg_warn("missing attribute %s in input from %s",
301
wanted_name, VSTREAM_PATH(fp));
302
return (conversions);
306
* See if the caller asks for this attribute.
308
if (wanted_type == ATTR_TYPE_HASH
309
|| (wanted_type != ATTR_TYPE_END
310
&& strcmp(wanted_name, STR(name_buf)) == 0))
312
if ((flags & ATTR_FLAG_EXTRA) != 0) {
313
msg_warn("unexpected attribute %s in input from %s",
314
STR(name_buf), VSTREAM_PATH(fp));
315
return (conversions);
319
* Skip over this attribute. The caller does not ask for it.
321
(void) attr_scan0_string(fp, str_buf, "input attribute value");
325
* Do the requested conversion.
327
switch (wanted_type) {
329
number = va_arg(ap, unsigned int *);
330
if ((ch = attr_scan0_number(fp, number, str_buf,
331
"input attribute value")) < 0)
335
long_number = va_arg(ap, unsigned long *);
336
if ((ch = attr_scan0_long_number(fp, long_number, str_buf,
337
"input attribute value")) < 0)
341
string = va_arg(ap, VSTRING *);
342
if ((ch = attr_scan0_string(fp, string,
343
"input attribute value")) < 0)
347
if ((ch = attr_scan0_string(fp, str_buf,
348
"input attribute value")) < 0)
350
if (htable_locate(hash_table, STR(name_buf)) != 0) {
351
if ((flags & ATTR_FLAG_EXTRA) != 0) {
352
msg_warn("duplicate attribute %s in input from %s",
353
STR(name_buf), VSTREAM_PATH(fp));
354
return (conversions);
356
} else if (hash_table->used >= ATTR_HASH_LIMIT) {
357
msg_warn("attribute count exceeds limit %d in input from %s",
358
ATTR_HASH_LIMIT, VSTREAM_PATH(fp));
359
return (conversions);
361
htable_enter(hash_table, STR(name_buf),
362
mystrdup(STR(str_buf)));
366
msg_panic("%s: unknown type code: %d", myname, wanted_type);
371
/* attr_scan0 - read attribute list from stream */
373
int attr_scan0(VSTREAM *fp, int flags,...)
379
ret = attr_vscan0(fp, flags, ap);
387
* Proof of concept test program. Mirror image of the attr_scan0 test
390
#include <msg_vstream.h>
392
int var_line_limit = 2048;
394
int main(int unused_argc, char **used_argv)
396
VSTRING *str_val = vstring_alloc(1);
397
HTABLE *table = htable_create(1);
398
HTABLE_INFO **ht_info_list;
405
msg_vstream_init(used_argv[0], VSTREAM_ERR);
406
if ((ret = attr_scan0(VSTREAM_IN,
408
ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
409
ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
410
ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
411
ATTR_TYPE_HASH, table,
412
ATTR_TYPE_END)) > 3) {
413
vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
414
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
415
vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
416
ht_info_list = htable_list(table);
417
for (ht = ht_info_list; *ht; ht++)
418
vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
419
myfree((char *) ht_info_list);
421
vstream_printf("return: %d\n", ret);
423
if ((ret = attr_scan0(VSTREAM_IN,
425
ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val,
426
ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val,
427
ATTR_TYPE_STR, ATTR_NAME_STR, str_val,
428
ATTR_TYPE_END)) == 3) {
429
vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val);
430
vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val);
431
vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val));
432
ht_info_list = htable_list(table);
433
for (ht = ht_info_list; *ht; ht++)
434
vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value);
435
myfree((char *) ht_info_list);
437
vstream_printf("return: %d\n", ret);
439
if (vstream_fflush(VSTREAM_OUT) != 0)
440
msg_fatal("write error: %m");
442
vstring_free(str_val);
443
htable_free(table, myfree);