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