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