1/*++ 2/* NAME 3/* attr_scan0 3 4/* SUMMARY 5/* recover attributes from byte stream 6/* SYNOPSIS 7/* #include <attr.h> 8/* 9/* int attr_scan0(fp, flags, type, name, ..., ATTR_TYPE_END) 10/* VSTREAM fp; 11/* int flags; 12/* int type; 13/* char *name; 14/* 15/* int attr_vscan0(fp, flags, ap) 16/* VSTREAM fp; 17/* int flags; 18/* va_list ap; 19/* DESCRIPTION 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(). 23/* 24/* attr_vscan0() provides an alternative interface that is convenient 25/* for calling from within a variadic function. 26/* 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: 30/* 31/* .in +5 32/* attr-list :== simple-attr* null 33/* .br 34/* simple-attr :== attr-name null attr-value null 35/* .br 36/* attr-name :== any string not containing null 37/* .br 38/* attr-value :== any string not containing null 39/* .br 40/* null :== the ASCII null character 41/* .in 42/* 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. 47/* 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. 52/* 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 57/* this is desirable. 58/* 59/* Arguments: 60/* .IP fp 61/* Stream to recover the input attributes from. 62/* .IP flags 63/* The bit-wise OR of zero or more of the following. 64/* .RS 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. 73/* .IP ATTR_FLAG_MORE 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 78/* terminator. 79/* .IP ATTR_FLAG_STRICT 80/* For convenience, this value combines both ATTR_FLAG_MISSING and 81/* ATTR_FLAG_EXTRA. 82/* .IP ATTR_FLAG_NONE 83/* For convenience, this value requests none of the above. 84/* .RE 85/* .IP type 86/* The type argument determines the arguments that follow. 87/* .RS 88/* .IP "ATTR_TYPE_INT (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_DATA (char *, VSTRING *)" 95/* This argument is followed by an attribute name and a VSTRING pointer. 96/* .IP "ATTR_TYPE_FUNC (ATTR_SCAN_SLAVE_FN, void *)" 97/* This argument is followed by a function pointer and a generic data 98/* pointer. The caller-specified function returns < 0 in case of 99/* error. 100/* .IP "ATTR_TYPE_HASH (HTABLE *)" 101/* .IP "ATTR_TYPE_NAMEVAL (NVTABLE *)" 102/* All further input attributes are processed as string attributes. 103/* No specific attribute sequence is enforced. 104/* All attributes up to the attribute list terminator are read, 105/* but only the first instance of each attribute is stored. 106/* There can be no more than 1024 attributes in a hash table. 107/* .sp 108/* The attribute string values are stored in the hash table under 109/* keys equal to the attribute name (obtained from the input stream). 110/* Values from the input stream are added to the hash table. Existing 111/* hash table entries are not replaced. 112/* .sp 113/* N.B. This construct must be followed by an ATTR_TYPE_END argument. 114/* .IP ATTR_TYPE_END 115/* This argument terminates the requested attribute list. 116/* .RE 117/* BUGS 118/* ATTR_TYPE_HASH (ATTR_TYPE_NAMEVAL) accepts attributes with arbitrary 119/* names from possibly untrusted sources. 120/* This is unsafe, unless the resulting table is queried only with 121/* known to be good attribute names. 122/* DIAGNOSTICS 123/* attr_scan0() and attr_vscan0() return -1 when malformed input is 124/* detected (string too long, incomplete line, missing end marker). 125/* Otherwise, the result value is the number of attributes that were 126/* successfully recovered from the input stream (a hash table counts 127/* as the number of entries stored into the table). 128/* 129/* Panic: interface violation. All system call errors are fatal. 130/* SEE ALSO 131/* attr_print0(3) send attributes over byte stream. 132/* LICENSE 133/* .ad 134/* .fi 135/* The Secure Mailer license must be distributed with this software. 136/* AUTHOR(S) 137/* Wietse Venema 138/* IBM T.J. Watson Research 139/* P.O. Box 704 140/* Yorktown Heights, NY 10598, USA 141/*--*/ 142 143/* System library. */ 144 145#include <sys_defs.h> 146#include <stdarg.h> 147#include <string.h> 148#include <stdio.h> 149 150/* Utility library. */ 151 152#include <msg.h> 153#include <mymalloc.h> 154#include <vstream.h> 155#include <vstring.h> 156#include <vstring_vstream.h> 157#include <htable.h> 158#include <base64_code.h> 159#include <attr.h> 160 161/* Application specific. */ 162 163#define STR(x) vstring_str(x) 164#define LEN(x) VSTRING_LEN(x) 165 166/* attr_scan0_string - pull a string from the input stream */ 167 168static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) 169{ 170 int ch; 171 172 if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) { 173 msg_warn("%s on %s while reading %s", 174 vstream_ftimeout(fp) ? "timeout" : "premature end-of-input", 175 VSTREAM_PATH(fp), context); 176 return (-1); 177 } 178 if (ch != 0) { 179 msg_warn("unexpected end-of-input from %s while reading %s", 180 VSTREAM_PATH(fp), context); 181 return (-1); 182 } 183 if (msg_verbose) 184 msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); 185 return (ch); 186} 187 188/* attr_scan0_data - pull a data blob from the input stream */ 189 190static int attr_scan0_data(VSTREAM *fp, VSTRING *str_buf, 191 const char *context) 192{ 193 static VSTRING *base64_buf = 0; 194 int ch; 195 196 if (base64_buf == 0) 197 base64_buf = vstring_alloc(10); 198 if ((ch = attr_scan0_string(fp, base64_buf, context)) < 0) 199 return (-1); 200 if (base64_decode(str_buf, STR(base64_buf), LEN(base64_buf)) == 0) { 201 msg_warn("malformed base64 data from %s while reading %s: %.100s", 202 VSTREAM_PATH(fp), context, STR(base64_buf)); 203 return (-1); 204 } 205 return (ch); 206} 207 208/* attr_scan0_number - pull a number from the input stream */ 209 210static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, 211 const char *context) 212{ 213 char junk = 0; 214 int ch; 215 216 if ((ch = attr_scan0_string(fp, str_buf, context)) < 0) 217 return (-1); 218 if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { 219 msg_warn("malformed numerical data from %s while reading %s: %.100s", 220 VSTREAM_PATH(fp), context, STR(str_buf)); 221 return (-1); 222 } 223 return (ch); 224} 225 226/* attr_scan0_long_number - pull a number from the input stream */ 227 228static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr, 229 VSTRING *str_buf, 230 const char *context) 231{ 232 char junk = 0; 233 int ch; 234 235 if ((ch = attr_scan0_string(fp, str_buf, context)) < 0) 236 return (-1); 237 if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) { 238 msg_warn("malformed numerical data from %s while reading %s: %.100s", 239 VSTREAM_PATH(fp), context, STR(str_buf)); 240 return (-1); 241 } 242 return (ch); 243} 244 245/* attr_vscan0 - receive attribute list from stream */ 246 247int attr_vscan0(VSTREAM *fp, int flags, va_list ap) 248{ 249 const char *myname = "attr_scan0"; 250 static VSTRING *str_buf = 0; 251 static VSTRING *name_buf = 0; 252 int wanted_type = -1; 253 char *wanted_name; 254 unsigned int *number; 255 unsigned long *long_number; 256 VSTRING *string; 257 HTABLE *hash_table; 258 int ch; 259 int conversions; 260 ATTR_SCAN_SLAVE_FN scan_fn; 261 void *scan_arg; 262 263 /* 264 * Sanity check. 265 */ 266 if (flags & ~ATTR_FLAG_ALL) 267 msg_panic("%s: bad flags: 0x%x", myname, flags); 268 269 /* 270 * EOF check. 271 */ 272 if ((ch = VSTREAM_GETC(fp)) == VSTREAM_EOF) 273 return (0); 274 vstream_ungetc(fp, ch); 275 276 /* 277 * Initialize. 278 */ 279 if (str_buf == 0) { 280 str_buf = vstring_alloc(10); 281 name_buf = vstring_alloc(10); 282 } 283 284 /* 285 * Iterate over all (type, name, value) triples. 286 */ 287 for (conversions = 0; /* void */ ; conversions++) { 288 289 /* 290 * Determine the next attribute type and attribute name on the 291 * caller's wish list. 292 * 293 * If we're reading into a hash table, we already know that the 294 * attribute value is string-valued, and we get the attribute name 295 * from the input stream instead. This is secure only when the 296 * resulting table is queried with known to be good attribute names. 297 */ 298 if (wanted_type != ATTR_TYPE_HASH) { 299 wanted_type = va_arg(ap, int); 300 if (wanted_type == ATTR_TYPE_END) { 301 if ((flags & ATTR_FLAG_MORE) != 0) 302 return (conversions); 303 wanted_name = "(list terminator)"; 304 } else if (wanted_type == ATTR_TYPE_HASH) { 305 wanted_name = "(any attribute name or list terminator)"; 306 hash_table = va_arg(ap, HTABLE *); 307 if (va_arg(ap, int) != ATTR_TYPE_END) 308 msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", 309 myname); 310 } else if (wanted_type != ATTR_TYPE_FUNC) { 311 wanted_name = va_arg(ap, char *); 312 } 313 } 314 315 /* 316 * Locate the next attribute of interest in the input stream. 317 */ 318 while (wanted_type != ATTR_TYPE_FUNC) { 319 320 /* 321 * Get the name of the next attribute. Hitting EOF is always bad. 322 * Hitting the end-of-input early is OK if the caller is prepared 323 * to deal with missing inputs. 324 */ 325 if (msg_verbose) 326 msg_info("%s: wanted attribute: %s", 327 VSTREAM_PATH(fp), wanted_name); 328 if ((ch = attr_scan0_string(fp, name_buf, 329 "input attribute name")) == VSTREAM_EOF) 330 return (-1); 331 if (LEN(name_buf) == 0) { 332 if (wanted_type == ATTR_TYPE_END 333 || wanted_type == ATTR_TYPE_HASH) 334 return (conversions); 335 if ((flags & ATTR_FLAG_MISSING) != 0) 336 msg_warn("missing attribute %s in input from %s", 337 wanted_name, VSTREAM_PATH(fp)); 338 return (conversions); 339 } 340 341 /* 342 * See if the caller asks for this attribute. 343 */ 344 if (wanted_type == ATTR_TYPE_HASH 345 || (wanted_type != ATTR_TYPE_END 346 && strcmp(wanted_name, STR(name_buf)) == 0)) 347 break; 348 if ((flags & ATTR_FLAG_EXTRA) != 0) { 349 msg_warn("unexpected attribute %s from %s (expecting: %s)", 350 STR(name_buf), VSTREAM_PATH(fp), wanted_name); 351 return (conversions); 352 } 353 354 /* 355 * Skip over this attribute. The caller does not ask for it. 356 */ 357 (void) attr_scan0_string(fp, str_buf, "input attribute value"); 358 } 359 360 /* 361 * Do the requested conversion. 362 */ 363 switch (wanted_type) { 364 case ATTR_TYPE_INT: 365 number = va_arg(ap, unsigned int *); 366 if ((ch = attr_scan0_number(fp, number, str_buf, 367 "input attribute value")) < 0) 368 return (-1); 369 break; 370 case ATTR_TYPE_LONG: 371 long_number = va_arg(ap, unsigned long *); 372 if ((ch = attr_scan0_long_number(fp, long_number, str_buf, 373 "input attribute value")) < 0) 374 return (-1); 375 break; 376 case ATTR_TYPE_STR: 377 string = va_arg(ap, VSTRING *); 378 if ((ch = attr_scan0_string(fp, string, 379 "input attribute value")) < 0) 380 return (-1); 381 break; 382 case ATTR_TYPE_DATA: 383 string = va_arg(ap, VSTRING *); 384 if ((ch = attr_scan0_data(fp, string, 385 "input attribute value")) < 0) 386 return (-1); 387 break; 388 case ATTR_TYPE_FUNC: 389 scan_fn = va_arg(ap, ATTR_SCAN_SLAVE_FN); 390 scan_arg = va_arg(ap, void *); 391 if (scan_fn(attr_scan0, fp, flags | ATTR_FLAG_MORE, scan_arg) < 0) 392 return (-1); 393 break; 394 case ATTR_TYPE_HASH: 395 if ((ch = attr_scan0_string(fp, str_buf, 396 "input attribute value")) < 0) 397 return (-1); 398 if (htable_locate(hash_table, STR(name_buf)) != 0) { 399 if ((flags & ATTR_FLAG_EXTRA) != 0) { 400 msg_warn("duplicate attribute %s in input from %s", 401 STR(name_buf), VSTREAM_PATH(fp)); 402 return (conversions); 403 } 404 } else if (hash_table->used >= ATTR_HASH_LIMIT) { 405 msg_warn("attribute count exceeds limit %d in input from %s", 406 ATTR_HASH_LIMIT, VSTREAM_PATH(fp)); 407 return (conversions); 408 } else { 409 htable_enter(hash_table, STR(name_buf), 410 mystrdup(STR(str_buf))); 411 } 412 break; 413 default: 414 msg_panic("%s: unknown type code: %d", myname, wanted_type); 415 } 416 } 417} 418 419/* attr_scan0 - read attribute list from stream */ 420 421int attr_scan0(VSTREAM *fp, int flags,...) 422{ 423 va_list ap; 424 int ret; 425 426 va_start(ap, flags); 427 ret = attr_vscan0(fp, flags, ap); 428 va_end(ap); 429 return (ret); 430} 431 432#ifdef TEST 433 434 /* 435 * Proof of concept test program. Mirror image of the attr_scan0 test 436 * program. 437 */ 438#include <msg_vstream.h> 439 440int var_line_limit = 2048; 441 442int main(int unused_argc, char **used_argv) 443{ 444 VSTRING *data_val = vstring_alloc(1); 445 VSTRING *str_val = vstring_alloc(1); 446 HTABLE *table = htable_create(1); 447 HTABLE_INFO **ht_info_list; 448 HTABLE_INFO **ht; 449 int int_val; 450 long long_val; 451 int ret; 452 453 msg_verbose = 1; 454 msg_vstream_init(used_argv[0], VSTREAM_ERR); 455 if ((ret = attr_scan0(VSTREAM_IN, 456 ATTR_FLAG_STRICT, 457 ATTR_TYPE_INT, ATTR_NAME_INT, &int_val, 458 ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, 459 ATTR_TYPE_STR, ATTR_NAME_STR, str_val, 460 ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val, 461 ATTR_TYPE_HASH, table, 462 ATTR_TYPE_END)) > 4) { 463 vstream_printf("%s %d\n", ATTR_NAME_INT, int_val); 464 vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); 465 vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); 466 vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(str_val)); 467 ht_info_list = htable_list(table); 468 for (ht = ht_info_list; *ht; ht++) 469 vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); 470 myfree((char *) ht_info_list); 471 } else { 472 vstream_printf("return: %d\n", ret); 473 } 474 if ((ret = attr_scan0(VSTREAM_IN, 475 ATTR_FLAG_STRICT, 476 ATTR_TYPE_INT, ATTR_NAME_INT, &int_val, 477 ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, 478 ATTR_TYPE_STR, ATTR_NAME_STR, str_val, 479 ATTR_TYPE_DATA, ATTR_NAME_DATA, data_val, 480 ATTR_TYPE_END)) == 4) { 481 vstream_printf("%s %d\n", ATTR_NAME_INT, int_val); 482 vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); 483 vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); 484 vstream_printf("%s %s\n", ATTR_NAME_DATA, STR(data_val)); 485 ht_info_list = htable_list(table); 486 for (ht = ht_info_list; *ht; ht++) 487 vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); 488 myfree((char *) ht_info_list); 489 } else { 490 vstream_printf("return: %d\n", ret); 491 } 492 if (vstream_fflush(VSTREAM_OUT) != 0) 493 msg_fatal("write error: %m"); 494 495 vstring_free(data_val); 496 vstring_free(str_val); 497 htable_free(table, myfree); 498 499 return (0); 500} 501 502#endif 503