diff options
| author | perry <perry@NetBSD.org> | 2002-02-02 23:10:24 +0000 |
|---|---|---|
| committer | perry <perry@NetBSD.org> | 2002-02-02 23:10:24 +0000 |
| commit | 6f59dc7aebbf705da79e61ef3fd24b48ed99d327 (patch) | |
| tree | c6e9fd356c8aac78b966a9dd53f5d0d14d5e62cd /gnu/dist/postfix/src/util | |
| parent | ecf43984517b4cfb837e78958769cc8207ea5288 (diff) | |
Postfix 1.1.2
(Postfix releases are now numbered -- 1.1.2 means 1.1, patchlevel 2.)
Lots of new features, same great security.
Diffstat (limited to 'gnu/dist/postfix/src/util')
54 files changed, 3383 insertions, 199 deletions
diff --git a/gnu/dist/postfix/src/util/alldig.c b/gnu/dist/postfix/src/util/alldig.c new file mode 100644 index 00000000000..c4815f68c67 --- /dev/null +++ b/gnu/dist/postfix/src/util/alldig.c @@ -0,0 +1,48 @@ +/*++ +/* NAME +/* alldig 3 +/* SUMMARY +/* predicate if string is all numerical +/* SYNOPSIS +/* #include <stringops.h> +/* +/* int alldig(string) +/* const char *string; +/* DESCRIPTION +/* alldig() determines if its argument is an all-numerical string. +/* SEE ALSO +/* An alldig() routine appears in Brian W. Kernighan, P.J. Plauger: +/* "Software Tools", Addison-Wesley 1976. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <ctype.h> + +/* Utility library. */ + +#include <stringops.h> + +/* alldig - return true if string is all digits */ + +int alldig(const char *string) +{ + const char *cp; + + if (*string == 0) + return (0); + for (cp = string; *cp != 0; cp++) + if (!ISDIGIT(*cp)) + return (0); + return (1); +} diff --git a/gnu/dist/postfix/src/util/attr.h b/gnu/dist/postfix/src/util/attr.h index e81381ed337..c656dadf8e1 100644 --- a/gnu/dist/postfix/src/util/attr.h +++ b/gnu/dist/postfix/src/util/attr.h @@ -5,19 +5,80 @@ /* NAME /* attr 3h /* SUMMARY -/* attribute list manager +/* attribute list manipulations /* SYNOPSIS -/* #include <attr.h> -/* DESCRIPTION -/* .nf +/* #include "attr.h" + DESCRIPTION + .nf /* - * External interface. + * System library. */ -extern void attr_enter(HTABLE *, const char *, const char *); -extern void attr_free(HTABLE *); +#include <stdarg.h> -#define attr_find(table, name) htable_find((table), (name)) + /* + * Utility library. + */ +#include <vstream.h> + + /* + * Attribute types. See attr_scan(3) for documentation. + */ +#define ATTR_TYPE_END 0 /* end of data */ +#define ATTR_TYPE_NUM 1 /* Unsigned integer */ +#define ATTR_TYPE_STR 2 /* Character string */ +#define ATTR_TYPE_HASH 3 /* Hash table */ +#define ATTR_TYPE_LONG 4 /* Unsigned long */ + + /* + * Flags that control processing. See attr_scan(3) for documentation. + */ +#define ATTR_FLAG_NONE 0 +#define ATTR_FLAG_MISSING (1<<0) /* Flag missing attribute */ +#define ATTR_FLAG_EXTRA (1<<1) /* Flag spurious attribute */ +#define ATTR_FLAG_MORE (1<<2) /* Don't skip or terminate */ + +#define ATTR_FLAG_STRICT (ATTR_FLAG_MISSING | ATTR_FLAG_EXTRA) +#define ATTR_FLAG_ALL (07) + +#define attr_print attr_print0 +#define attr_vprint attr_vprint0 +#define attr_scan attr_scan0 +#define attr_vscan attr_vscan0 + + /* + * attr_print64.c. + */ +extern int attr_print64(VSTREAM *, int,...); +extern int attr_vprint64(VSTREAM *, int, va_list); + + /* + * attr_scan64.c. + */ +extern int attr_scan64(VSTREAM *, int,...); +extern int attr_vscan64(VSTREAM *, int, va_list); + + /* + * attr_print0.c. + */ +extern int attr_print0(VSTREAM *, int,...); +extern int attr_vprint0(VSTREAM *, int, va_list); + + /* + * attr_scan0.c. + */ +extern int attr_scan0(VSTREAM *, int,...); +extern int attr_vscan0(VSTREAM *, int, va_list); + + /* + * Attribute names for testing the compatibility of the read and write + * routines. + */ +#ifdef TEST +#define ATTR_NAME_NUM "number" +#define ATTR_NAME_STR "string" +#define ATTR_NAME_LONG "long_number" +#endif /* LICENSE /* .ad diff --git a/gnu/dist/postfix/src/util/attr_print0.c b/gnu/dist/postfix/src/util/attr_print0.c new file mode 100644 index 00000000000..c39b72104da --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_print0.c @@ -0,0 +1,209 @@ +/*++ +/* NAME +/* attr_print0 3 +/* SUMMARY +/* send attributes over byte stream +/* SYNOPSIS +/* #include <attr.h> +/* +/* int attr_print0(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vprint0(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_print0() takes zero or more (name, value) simple attributes +/* or (name, count, value) list attributes, and converts its input +/* to a byte stream that can be recovered with attr_scan0(). The stream +/* is not flushed. +/* +/* attr_vprint0() provides an alternate interface that is convenient +/* for calling from within variadoc functions. +/* +/* Attributes are sent in the requested order as specified with the +/* attr_print0() argument list. This routine satisfies the formatting +/* rules as outlined in attr_scan0(3). +/* +/* Arguments: +/* .IP fp +/* Stream to write the result to. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MORE +/* After sending the requested attributes, leave the output stream in +/* a state that is usable for more attribute sending operations on +/* the same output attribute list. +/* By default, attr_print0() automatically appends an attribute list +/* terminator when it has sent the last requested attribute. +/* .RE +/* .IP type +/* The type determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int)" +/* This argument is followed by an attribute name and an integer. +/* .IP "ATTR_TYPE_NUM (char *, long)" +/* This argument is followed by an attribute name and a long integer. +/* .IP "ATTR_TYPE_STR (char *, char *)" +/* This argument is followed by an attribute name and a null-terminated +/* string. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* The content of the hash table is sent as a sequence of string-valued +/* attributes with names equal to the hash table lookup key. +/* .IP ATTR_TYPE_END +/* This terminates the attribute list. +/* .RE +/* DIAGNOSTICS +/* The result value is 0 in case of success, VSTREAM_EOF in case +/* of trouble. +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_scan0(3) recover attributes from byte stream +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <htable.h> +#include <attr.h> + +/* attr_vprint0 - send attribute list to stream */ + +int attr_vprint0(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_print0"; + int attr_type; + char *attr_name; + unsigned int_val; + unsigned long long_val; + char *str_val; + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Iterate over all (type, name, value) triples, and produce output on + * the fly. + */ + while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (attr_type) { + case ATTR_TYPE_NUM: + attr_name = va_arg(ap, char *); + vstream_fwrite(fp, attr_name, strlen(attr_name) + 1); + int_val = va_arg(ap, int); + vstream_fprintf(fp, "%u", (unsigned) int_val); + VSTREAM_PUTC('\0', fp); + if (msg_verbose) + msg_info("send attr %s = %u", attr_name, int_val); + break; + case ATTR_TYPE_LONG: + attr_name = va_arg(ap, char *); + vstream_fwrite(fp, attr_name, strlen(attr_name) + 1); + long_val = va_arg(ap, unsigned long); + vstream_fprintf(fp, "%lu", (unsigned long) long_val); + VSTREAM_PUTC('\0', fp); + if (msg_verbose) + msg_info("send attr %s = %lu", attr_name, long_val); + break; + case ATTR_TYPE_STR: + attr_name = va_arg(ap, char *); + vstream_fwrite(fp, attr_name, strlen(attr_name) + 1); + str_val = va_arg(ap, char *); + vstream_fwrite(fp, str_val, strlen(str_val) + 1); + if (msg_verbose) + msg_info("send attr %s = %s", attr_name, str_val); + break; + case ATTR_TYPE_HASH: + ht_info_list = htable_list(va_arg(ap, HTABLE *)); + for (ht = ht_info_list; *ht; ht++) { + vstream_fwrite(fp, ht[0]->key, strlen(ht[0]->key) + 1); + vstream_fwrite(fp, ht[0]->value, strlen(ht[0]->value) + 1); + if (msg_verbose) + msg_info("send attr name %s value %s", + ht[0]->key, ht[0]->value); + } + myfree((char *) ht_info_list); + break; + default: + msg_panic("%s: unknown type code: %d", myname, attr_type); + } + } + if ((flags & ATTR_FLAG_MORE) == 0) + VSTREAM_PUTC('\0', fp); + return (vstream_ferror(fp)); +} + +int attr_print0(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vprint0(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan0 test + * program. + */ +#include <msg_vstream.h> + +int main(int unused_argc, char **argv) +{ + HTABLE *table = htable_create(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + htable_enter(table, "foo-name", mystrdup("foo-value")); + htable_enter(table, "bar-name", mystrdup("bar-value")); + attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_HASH, table, + ATTR_TYPE_END); + attr_print0(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_END); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + htable_free(table, myfree); + return (0); +} + +#endif diff --git a/gnu/dist/postfix/src/util/attr_print64.c b/gnu/dist/postfix/src/util/attr_print64.c new file mode 100644 index 00000000000..1fc37d7b346 --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_print64.c @@ -0,0 +1,253 @@ +/*++ +/* NAME +/* attr_print64 3 +/* SUMMARY +/* send attributes over byte stream +/* SYNOPSIS +/* #include <attr.h> +/* +/* int attr_print64(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vprint64(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_print64() takes zero or more (name, value) simple attributes +/* or (name, count, value) list attributes, and converts its input +/* to a byte stream that can be recovered with attr_scan64(). The stream +/* is not flushed. +/* +/* attr_vprint64() provides an alternate interface that is convenient +/* for calling from within variadoc functions. +/* +/* Attributes are sent in the requested order as specified with the +/* attr_print64() argument list. This routine satisfies the formatting +/* rules as outlined in attr_scan64(3). +/* +/* Arguments: +/* .IP fp +/* Stream to write the result to. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MORE +/* After sending the requested attributes, leave the output stream in +/* a state that is usable for more attribute sending operations on +/* the same output attribute list. +/* By default, attr_print64() automatically appends an attribute list +/* terminator when it has sent the last requested attribute. +/* .RE +/* .IP type +/* The type determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int)" +/* This argument is followed by an attribute name and an integer. +/* .IP "ATTR_TYPE_NUM (char *, long)" +/* This argument is followed by an attribute name and a long integer. +/* .IP "ATTR_TYPE_STR (char *, char *)" +/* This argument is followed by an attribute name and a null-terminated +/* string. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* The content of the hash table is sent as a sequence of string-valued +/* attributes with names equal to the hash table lookup key. +/* .IP ATTR_TYPE_END +/* This terminates the attribute list. +/* .RE +/* DIAGNOSTICS +/* The result value is 0 in case of success, VSTREAM_EOF in case +/* of trouble. +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_scan64(3) recover attributes from byte stream +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <htable.h> +#include <base64_code.h> +#include <attr.h> + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_print64_str - encode and send attribute information */ + +static void attr_print64_str(VSTREAM *fp, const char *str, int len) +{ + static VSTRING *base64_buf; + + if (base64_buf == 0) + base64_buf = vstring_alloc(10); + + base64_encode(base64_buf, str, len); + vstream_fputs(STR(base64_buf), fp); +} + +static void attr_print64_num(VSTREAM *fp, unsigned num) +{ + static VSTRING *plain; + + if (plain == 0) + plain = vstring_alloc(10); + + vstring_sprintf(plain, "%u", num); + attr_print64_str(fp, STR(plain), LEN(plain)); +} + +static void attr_print64_long_num(VSTREAM *fp, unsigned long long_num) +{ + static VSTRING *plain; + + if (plain == 0) + plain = vstring_alloc(10); + + vstring_sprintf(plain, "%lu", long_num); + attr_print64_str(fp, STR(plain), LEN(plain)); +} + +/* attr_vprint64 - send attribute list to stream */ + +int attr_vprint64(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_print64"; + int attr_type; + char *attr_name; + unsigned int_val; + unsigned long long_val; + char *str_val; + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Iterate over all (type, name, value) triples, and produce output on + * the fly. + */ + while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { + switch (attr_type) { + case ATTR_TYPE_NUM: + attr_name = va_arg(ap, char *); + attr_print64_str(fp, attr_name, strlen(attr_name)); + int_val = va_arg(ap, int); + VSTREAM_PUTC(':', fp); + attr_print64_num(fp, (unsigned) int_val); + if (msg_verbose) + msg_info("send attr %s = %u", attr_name, int_val); + break; + case ATTR_TYPE_LONG: + attr_name = va_arg(ap, char *); + attr_print64_str(fp, attr_name, strlen(attr_name)); + long_val = va_arg(ap, long); + VSTREAM_PUTC(':', fp); + attr_print64_long_num(fp, (unsigned long) long_val); + if (msg_verbose) + msg_info("send attr %s = %lu", attr_name, long_val); + break; + case ATTR_TYPE_STR: + attr_name = va_arg(ap, char *); + attr_print64_str(fp, attr_name, strlen(attr_name)); + str_val = va_arg(ap, char *); + VSTREAM_PUTC(':', fp); + attr_print64_str(fp, str_val, strlen(str_val)); + if (msg_verbose) + msg_info("send attr %s = %s", attr_name, str_val); + break; + case ATTR_TYPE_HASH: + ht_info_list = htable_list(va_arg(ap, HTABLE *)); + for (ht = ht_info_list; *ht; ht++) { + attr_print64_str(fp, ht[0]->key, strlen(ht[0]->key)); + VSTREAM_PUTC(':', fp); + attr_print64_str(fp, ht[0]->value, strlen(ht[0]->value)); + if (msg_verbose) + msg_info("send attr name %s value %s", + ht[0]->key, ht[0]->value); + if (ht[1] != 0) + VSTREAM_PUTC('\n', fp); + } + myfree((char *) ht_info_list); + break; + default: + msg_panic("%s: unknown type code: %d", myname, attr_type); + } + VSTREAM_PUTC('\n', fp); + } + if ((flags & ATTR_FLAG_MORE) == 0) + VSTREAM_PUTC('\n', fp); + return (vstream_ferror(fp)); +} + +int attr_print64(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vprint64(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan64 test + * program. + */ +#include <msg_vstream.h> + +int main(int unused_argc, char **argv) +{ + HTABLE *table = htable_create(1); + + msg_vstream_init(argv[0], VSTREAM_ERR); + msg_verbose = 1; + htable_enter(table, "foo-name", mystrdup("foo-value")); + htable_enter(table, "bar-name", mystrdup("bar-value")); + attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_HASH, table, + ATTR_TYPE_END); + attr_print64(VSTREAM_OUT, ATTR_FLAG_NONE, + ATTR_TYPE_NUM, ATTR_NAME_NUM, 4711, + ATTR_TYPE_LONG, ATTR_NAME_LONG, 1234, + ATTR_TYPE_STR, ATTR_NAME_STR, "whoopee", + ATTR_TYPE_END); + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + htable_free(table, myfree); + return (0); +} + +#endif diff --git a/gnu/dist/postfix/src/util/attr_scan.ref b/gnu/dist/postfix/src/util/attr_scan.ref new file mode 100644 index 00000000000..cd06a27d82d --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_scan.ref @@ -0,0 +1,36 @@ +./attr_print: send attr number = 4711 +./attr_print: send attr string = whoopee +./attr_print: send attr name foo-name value foo-value +./attr_print: send attr name bar-name value bar-value +./attr_print: send attr number = 4711 +./attr_print: send attr string = whoopee +./attr_scan: unknown_stream: wanted attribute: number +./attr_scan: input attribute name: number +./attr_scan: input attribute value: 4711 +./attr_scan: unknown_stream: wanted attribute: string +./attr_scan: input attribute name: string +./attr_scan: input attribute value: whoopee +./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan: input attribute name: foo-name +./attr_scan: input attribute value: foo-value +./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan: input attribute name: bar-name +./attr_scan: input attribute value: bar-value +./attr_scan: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan: input attribute name: (end) +./attr_scan: unknown_stream: wanted attribute: number +./attr_scan: input attribute name: number +./attr_scan: input attribute value: 4711 +./attr_scan: unknown_stream: wanted attribute: string +./attr_scan: input attribute name: string +./attr_scan: input attribute value: whoopee +./attr_scan: unknown_stream: wanted attribute: (list terminator) +./attr_scan: input attribute name: (end) +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/gnu/dist/postfix/src/util/attr_scan0.c b/gnu/dist/postfix/src/util/attr_scan0.c new file mode 100644 index 00000000000..ea4cf880886 --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_scan0.c @@ -0,0 +1,441 @@ +/*++ +/* NAME +/* attr_scan0 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include <attr.h> +/* +/* int attr_scan0(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vscan0(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_scan0() takes zero or more (name, value) request attributes +/* and recovers the attribute values from the byte stream that was +/* possibly generated by attr_print0(). +/* +/* attr_vscan0() provides an alternative interface that is convenient +/* for calling from within a variadic function. +/* +/* The input stream is formatted as follows, where (item)* stands +/* for zero or more instances of the specified item, and where +/* (item1 | item2) stands for choice: +/* +/* .in +5 +/* attr-list :== simple-attr* null +/* .br +/* simple-attr :== attr-name null attr-value null +/* .br +/* attr-name :== any string not containing null +/* .br +/* attr-value :== any string not containing null +/* .br +/* null :== the ASCII null character +/* .in +/* +/* All attribute names and attribute values are sent as null terminated +/* strings. Each string must be no longer than 2*var_line_limit +/* characters. The formatting rules favor implementations in C. +/* +/* Normally, attributes must be received in the sequence as specified with +/* the attr_scan0() argument list. The input stream may contain additional +/* attributes at any point in the input stream, including additional +/* instances of requested attributes. +/* +/* Additional input attributes or input attribute instances are silently +/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified +/* (see below). This allows for some flexibility in the evolution of +/* protocols while still providing the option of being strict where +/* this is desirable. +/* +/* Arguments: +/* .IP fp +/* Stream to recover the input attributes from. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MISSING +/* Log a warning when the input attribute list terminates before all +/* requested attributes are recovered. It is always an error when the +/* input stream ends without the newline attribute list terminator. +/* .IP ATTR_FLAG_EXTRA +/* Log a warning and stop attribute recovery when the input stream +/* contains an attribute that was not requested. This includes the +/* case of additional instances of a requested attribute. +/* .IP ATTR_FLAG_MORE +/* After recovering the requested attributes, leave the input stream +/* in a state that is usable for more attr_scan0() operations from the +/* same input attribute list. +/* By default, attr_scan0() skips forward past the input attribute list +/* terminator. +/* .IP ATTR_FLAG_STRICT +/* For convenience, this value combines both ATTR_FLAG_MISSING and +/* ATTR_FLAG_EXTRA. +/* .IP ATTR_FLAG_NONE +/* For convenience, this value requests none of the above. +/* .RE +/* .IP type +/* The type argument determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int *)" +/* This argument is followed by an attribute name and an integer pointer. +/* .IP "ATTR_TYPE_LONG (char *, long *)" +/* This argument is followed by an attribute name and a long pointer. +/* .IP "ATTR_TYPE_STR (char *, VSTRING *)" +/* This argument is followed by an attribute name and a VSTRING pointer. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* All further input attributes are processed as string attributes. +/* No specific attribute sequence is enforced. +/* All attributes up to the attribute list terminator are read, +/* but only the first instance of each attribute is stored. +/* .sp +/* The attribute string values are stored in the hash table under +/* keys equal to the attribute name (obtained from the input stream). +/* Values from the input stream are added to the hash table. Existing +/* hash table entries are not replaced. +/* .sp +/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* .IP ATTR_TYPE_END +/* This argument terminates the requested attribute list. +/* .RE +/* BUGS +/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly +/* untrusted sources. This is unsafe, unless the resulting table is +/* queried only with known to be good attribute names. +/* DIAGNOSTICS +/* attr_scan0() and attr_vscan0() return -1 when malformed input is +/* detected (string too long, incomplete line, missing end marker). +/* Otherwise, the result value is the number of attributes that were +/* successfully recovered from the input stream (a hash table counts +/* as the number of entries stored into the table). +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_print0(3) send attributes over byte stream. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> +#include <vstring_vstream.h> +#include <htable.h> +#include <attr.h> + +/* Application specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_scan0_string - pull a string from the input stream */ + +static int attr_scan0_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) +{ + extern int var_line_limit; /* XXX */ + int limit = var_line_limit * 2; + int ch; + + if ((ch = vstring_get_null(plain_buf, fp)) == VSTREAM_EOF) { + msg_warn("premature end-of-input from %s while reading %s", + VSTREAM_PATH(fp), context); + return (-1); + } + if (ch != 0) { + msg_warn("string length > %d characters from %s while reading %s", + limit, VSTREAM_PATH(fp), context); + return (-1); + } + if (msg_verbose) + msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); + return (ch); +} + +/* attr_scan0_number - pull a number from the input stream */ + +static int attr_scan0_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan0_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_scan0_long_number - pull a number from the input stream */ + +static int attr_scan0_long_number(VSTREAM *fp, unsigned long *ptr, + VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan0_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_vscan0 - receive attribute list from stream */ + +int attr_vscan0(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_scan0"; + static VSTRING *str_buf = 0; + static VSTRING *name_buf = 0; + int wanted_type = -1; + char *wanted_name; + unsigned int *number; + unsigned long *long_number; + VSTRING *string; + HTABLE *hash_table; + int ch; + int conversions; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Initialize. + */ + if (str_buf == 0) { + str_buf = vstring_alloc(10); + name_buf = vstring_alloc(10); + } + + /* + * Iterate over all (type, name, value) triples. + */ + for (conversions = 0; /* void */ ; conversions++) { + + /* + * Determine the next attribute type and attribute name on the + * caller's wish list. + * + * If we're reading into a hash table, we already know that the + * attribute value is string-valued, and we get the attribute name + * from the input stream instead. This is secure only when the + * resulting table is queried with known to be good attribute names. + */ + if (wanted_type != ATTR_TYPE_HASH) { + wanted_type = va_arg(ap, int); + if (wanted_type == ATTR_TYPE_END) { + if ((flags & ATTR_FLAG_MORE) != 0) + return (conversions); + wanted_name = "(list terminator)"; + } else if (wanted_type == ATTR_TYPE_HASH) { + wanted_name = "(any attribute name or list terminator)"; + hash_table = va_arg(ap, HTABLE *); + if (va_arg(ap, int) !=ATTR_TYPE_END) + msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", + myname); + } else { + wanted_name = va_arg(ap, char *); + } + } + + /* + * Locate the next attribute of interest in the input stream. + */ + for (;;) { + + /* + * Get the name of the next attribute. Hitting EOF is always bad. + * Hitting the end-of-input early is OK if the caller is prepared + * to deal with missing inputs. + */ + if (msg_verbose) + msg_info("%s: wanted attribute: %s", + VSTREAM_PATH(fp), wanted_name); + if ((ch = attr_scan0_string(fp, name_buf, + "input attribute name")) == VSTREAM_EOF) + return (-1); + if (LEN(name_buf) == 0) { + if (wanted_type == ATTR_TYPE_END + || wanted_type == ATTR_TYPE_HASH) + return (conversions); + if ((flags & ATTR_FLAG_MISSING) != 0) + msg_warn("missing attribute %s in input from %s", + wanted_name, VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * See if the caller asks for this attribute. + */ + if (wanted_type == ATTR_TYPE_HASH + || (wanted_type != ATTR_TYPE_END + && strcmp(wanted_name, STR(name_buf)) == 0)) + break; + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("spurious attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * Skip over this attribute. The caller does not ask for it. + */ + (void) attr_scan0_string(fp, str_buf, "input attribute value"); + } + + /* + * Do the requested conversion. + */ + switch (wanted_type) { + case ATTR_TYPE_NUM: + number = va_arg(ap, unsigned int *); + if ((ch = attr_scan0_number(fp, number, str_buf, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_LONG: + long_number = va_arg(ap, unsigned long *); + if ((ch = attr_scan0_long_number(fp, long_number, str_buf, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_STR: + string = va_arg(ap, VSTRING *); + if ((ch = attr_scan0_string(fp, string, + "input attribute value")) < 0) + return (-1); + break; + case ATTR_TYPE_HASH: + if ((ch = attr_scan0_string(fp, str_buf, + "input attribute value")) < 0) + return (-1); + if (htable_locate(hash_table, STR(name_buf)) != 0) { + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("duplicate attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + } else { + htable_enter(hash_table, STR(name_buf), + mystrdup(STR(str_buf))); + } + break; + default: + msg_panic("%s: unknown type code: %d", myname, wanted_type); + } + } +} + +/* attr_scan0 - read attribute list from stream */ + +int attr_scan0(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vscan0(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan0 test + * program. + */ +#include <msg_vstream.h> + +int var_line_limit = 2048; + +int main(int unused_argc, char **used_argv) +{ + VSTRING *str_val = vstring_alloc(1); + HTABLE *table = htable_create(1); + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + int int_val; + long long_val; + int ret; + + msg_verbose = 1; + msg_vstream_init(used_argv[0], VSTREAM_ERR); + if ((ret = attr_scan0(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_HASH, table, + ATTR_TYPE_END)) > 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if ((ret = attr_scan0(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_END)) == 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + vstring_free(str_val); + htable_free(table, myfree); + + return (0); +} + +#endif diff --git a/gnu/dist/postfix/src/util/attr_scan0.ref b/gnu/dist/postfix/src/util/attr_scan0.ref new file mode 100644 index 00000000000..105382fd1ef --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_scan0.ref @@ -0,0 +1,46 @@ +./attr_print0: send attr number = 4711 +./attr_print0: send attr long_number = 1234 +./attr_print0: send attr string = whoopee +./attr_print0: send attr name foo-name value foo-value +./attr_print0: send attr name bar-name value bar-value +./attr_print0: send attr number = 4711 +./attr_print0: send attr long_number = 1234 +./attr_print0: send attr string = whoopee +./attr_scan0: unknown_stream: wanted attribute: number +./attr_scan0: input attribute name: number +./attr_scan0: input attribute value: 4711 +./attr_scan0: unknown_stream: wanted attribute: long_number +./attr_scan0: input attribute name: long_number +./attr_scan0: input attribute value: 1234 +./attr_scan0: unknown_stream: wanted attribute: string +./attr_scan0: input attribute name: string +./attr_scan0: input attribute value: whoopee +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: input attribute name: foo-name +./attr_scan0: input attribute value: foo-value +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: input attribute name: bar-name +./attr_scan0: input attribute value: bar-value +./attr_scan0: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan0: input attribute name: (end) +./attr_scan0: unknown_stream: wanted attribute: number +./attr_scan0: input attribute name: number +./attr_scan0: input attribute value: 4711 +./attr_scan0: unknown_stream: wanted attribute: long_number +./attr_scan0: input attribute name: long_number +./attr_scan0: input attribute value: 1234 +./attr_scan0: unknown_stream: wanted attribute: string +./attr_scan0: input attribute name: string +./attr_scan0: input attribute value: whoopee +./attr_scan0: unknown_stream: wanted attribute: (list terminator) +./attr_scan0: input attribute name: (end) +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/gnu/dist/postfix/src/util/attr_scan64.c b/gnu/dist/postfix/src/util/attr_scan64.c new file mode 100644 index 00000000000..d8631ef1cac --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_scan64.c @@ -0,0 +1,506 @@ +/*++ +/* NAME +/* attr_scan64 3 +/* SUMMARY +/* recover attributes from byte stream +/* SYNOPSIS +/* #include <attr.h> +/* +/* int attr_scan64(fp, flags, type, name, ...) +/* VSTREAM fp; +/* int flags; +/* int type; +/* char *name; +/* +/* int attr_vscan64(fp, flags, ap) +/* VSTREAM fp; +/* int flags; +/* va_list ap; +/* DESCRIPTION +/* attr_scan64() takes zero or more (name, value) request attributes +/* and recovers the attribute values from the byte stream that was +/* possibly generated by attr_print64(). +/* +/* attr_vscan64() provides an alternative interface that is convenient +/* for calling from within a variadic function. +/* +/* The input stream is formatted as follows, where (item)* stands +/* for zero or more instances of the specified item, and where +/* (item1 | item2) stands for choice: +/* +/* .in +5 +/* attr-list :== simple-attr* newline +/* .br +/* simple-attr :== attr-name colon attr-value newline +/* .br +/* attr-name :== any base64 encoded string +/* .br +/* attr-value :== any base64 encoded string +/* .br +/* colon :== the ASCII colon character +/* .br +/* newline :== the ASCII newline character +/* .in +/* +/* All attribute names and attribute values are sent as base64-encoded +/* strings. Each base64 encoding must be no longer than 2*var_line_limit +/* characters. The formatting rules aim to make implementations in PERL +/* and other languages easy. +/* +/* Normally, attributes must be received in the sequence as specified with +/* the attr_scan64() argument list. The input stream may contain additional +/* attributes at any point in the input stream, including additional +/* instances of requested attributes. +/* +/* Additional input attributes or input attribute instances are silently +/* skipped over, unless the ATTR_FLAG_EXTRA processing flag is specified +/* (see below). This allows for some flexibility in the evolution of +/* protocols while still providing the option of being strict where +/* this is desirable. +/* +/* Arguments: +/* .IP fp +/* Stream to recover the input attributes from. +/* .IP flags +/* The bit-wise OR of zero or more of the following. +/* .RS +/* .IP ATTR_FLAG_MISSING +/* Log a warning when the input attribute list terminates before all +/* requested attributes are recovered. It is always an error when the +/* input stream ends without the newline attribute list terminator. +/* .IP ATTR_FLAG_EXTRA +/* Log a warning and stop attribute recovery when the input stream +/* contains an attribute that was not requested. This includes the +/* case of additional instances of a requested attribute. +/* .IP ATTR_FLAG_MORE +/* After recovering the requested attributes, leave the input stream +/* in a state that is usable for more attr_scan64() operations from the +/* same input attribute list. +/* By default, attr_scan64() skips forward past the input attribute list +/* terminator. +/* .IP ATTR_FLAG_STRICT +/* For convenience, this value combines both ATTR_FLAG_MISSING and +/* ATTR_FLAG_EXTRA. +/* .IP ATTR_FLAG_NONE +/* For convenience, this value requests none of the above. +/* .RE +/* .IP type +/* The type argument determines the arguments that follow. +/* .RS +/* .IP "ATTR_TYPE_NUM (char *, int *)" +/* This argument is followed by an attribute name and an integer pointer. +/* .IP "ATTR_TYPE_LONG (char *, long *)" +/* This argument is followed by an attribute name and a long pointer. +/* .IP "ATTR_TYPE_STR (char *, VSTRING *)" +/* This argument is followed by an attribute name and a VSTRING pointer. +/* .IP "ATTR_TYPE_HASH (HTABLE *)" +/* All further input attributes are processed as string attributes. +/* No specific attribute sequence is enforced. +/* All attributes up to the attribute list terminator are read, +/* but only the first instance of each attribute is stored. +/* .sp +/* The attribute string values are stored in the hash table under +/* keys equal to the attribute name (obtained from the input stream). +/* Values from the input stream are added to the hash table. Existing +/* hash table entries are not replaced. +/* .sp +/* N.B. This construct must be followed by an ATTR_TYPE_END argument. +/* .IP ATTR_TYPE_END +/* This argument terminates the requested attribute list. +/* .RE +/* BUGS +/* ATTR_TYPE_HASH accepts attributes with arbitrary names from possibly +/* untrusted sources. This is unsafe, unless the resulting table is +/* queried only with known to be good attribute names. +/* DIAGNOSTICS +/* attr_scan64() and attr_vscan64() return -1 when malformed input is +/* detected (string too long, incomplete line, missing end marker). +/* Otherwise, the result value is the number of attributes that were +/* successfully recovered from the input stream (a hash table counts +/* as the number of entries stored into the table). +/* +/* Panic: interface violation. All system call errors are fatal. +/* SEE ALSO +/* attr_print64(3) send attributes over byte stream. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstream.h> +#include <vstring.h> +#include <htable.h> +#include <base64_code.h> +#include <attr.h> + +/* Application specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* attr_scan64_string - pull a string from the input stream */ + +static int attr_scan64_string(VSTREAM *fp, VSTRING *plain_buf, const char *context) +{ + static VSTRING *base64_buf = 0; + extern int var_line_limit; /* XXX */ + int limit = var_line_limit * 2; + int ch; + + if (base64_buf == 0) + base64_buf = vstring_alloc(10); + + VSTRING_RESET(base64_buf); + while ((ch = VSTREAM_GETC(fp)) != ':' && ch != '\n') { + if (ch == VSTREAM_EOF) { + msg_warn("premature end-of-input from %s while reading %s", + VSTREAM_PATH(fp), context); + return (-1); + } + VSTRING_ADDCH(base64_buf, ch); +#if 0 + if (LEN(base64_buf) > limit) { + msg_warn("string length > %d characters from %s while reading %s", + limit, VSTREAM_PATH(fp), context); + return (-1); + } +#endif + } + VSTRING_TERMINATE(base64_buf); + if (base64_decode(plain_buf, STR(base64_buf), LEN(base64_buf)) == 0) { + msg_warn("malformed base64 data from %s: %.100s", + VSTREAM_PATH(fp), STR(base64_buf)); + return (-1); + } + if (msg_verbose) + msg_info("%s: %s", context, *STR(plain_buf) ? STR(plain_buf) : "(end)"); + return (ch); +} + +/* attr_scan64_number - pull a number from the input stream */ + +static int attr_scan64_number(VSTREAM *fp, unsigned *ptr, VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan64_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%u%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_scan64_long_number - pull a number from the input stream */ + +static int attr_scan64_long_number(VSTREAM *fp, unsigned long *ptr, + VSTRING *str_buf, + const char *context) +{ + char junk = 0; + int ch; + + if ((ch = attr_scan64_string(fp, str_buf, context)) < 0) + return (-1); + if (sscanf(STR(str_buf), "%lu%c", ptr, &junk) != 1 || junk != 0) { + msg_warn("malformed numerical data from %s while reading %s: %.100s", + VSTREAM_PATH(fp), context, STR(str_buf)); + return (-1); + } + return (ch); +} + +/* attr_vscan64 - receive attribute list from stream */ + +int attr_vscan64(VSTREAM *fp, int flags, va_list ap) +{ + const char *myname = "attr_scan64"; + static VSTRING *str_buf = 0; + static VSTRING *name_buf = 0; + int wanted_type = -1; + char *wanted_name; + unsigned int *number; + unsigned long *long_number; + VSTRING *string; + HTABLE *hash_table; + int ch; + int conversions; + + /* + * Sanity check. + */ + if (flags & ~ATTR_FLAG_ALL) + msg_panic("%s: bad flags: 0x%x", myname, flags); + + /* + * Initialize. + */ + if (str_buf == 0) { + str_buf = vstring_alloc(10); + name_buf = vstring_alloc(10); + } + + /* + * Iterate over all (type, name, value) triples. + */ + for (conversions = 0; /* void */ ; conversions++) { + + /* + * Determine the next attribute type and attribute name on the + * caller's wish list. + * + * If we're reading into a hash table, we already know that the + * attribute value is string-valued, and we get the attribute name + * from the input stream instead. This is secure only when the + * resulting table is queried with known to be good attribute names. + */ + if (wanted_type != ATTR_TYPE_HASH) { + wanted_type = va_arg(ap, int); + if (wanted_type == ATTR_TYPE_END) { + if ((flags & ATTR_FLAG_MORE) != 0) + return (conversions); + wanted_name = "(list terminator)"; + } else if (wanted_type == ATTR_TYPE_HASH) { + wanted_name = "(any attribute name or list terminator)"; + hash_table = va_arg(ap, HTABLE *); + if (va_arg(ap, int) !=ATTR_TYPE_END) + msg_panic("%s: ATTR_TYPE_HASH not followed by ATTR_TYPE_END", + myname); + } else { + wanted_name = va_arg(ap, char *); + } + } + + /* + * Locate the next attribute of interest in the input stream. + */ + for (;;) { + + /* + * Get the name of the next attribute. Hitting EOF is always bad. + * Hitting the end-of-input early is OK if the caller is prepared + * to deal with missing inputs. + */ + if (msg_verbose) + msg_info("%s: wanted attribute: %s", + VSTREAM_PATH(fp), wanted_name); + if ((ch = attr_scan64_string(fp, name_buf, + "input attribute name")) == VSTREAM_EOF) + return (-1); + if (ch == '\n' && LEN(name_buf) == 0) { + if (wanted_type == ATTR_TYPE_END + || wanted_type == ATTR_TYPE_HASH) + return (conversions); + if ((flags & ATTR_FLAG_MISSING) != 0) + msg_warn("missing attribute %s in input from %s", + wanted_name, VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * See if the caller asks for this attribute. + */ + if (wanted_type == ATTR_TYPE_HASH + || (wanted_type != ATTR_TYPE_END + && strcmp(wanted_name, STR(name_buf)) == 0)) + break; + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("spurious attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + + /* + * Skip over this attribute. The caller does not ask for it. + */ + while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') + /* void */ ; + } + + /* + * Do the requested conversion. If the target attribute is a + * non-array type, disallow sending a multi-valued attribute, and + * disallow sending no value. If the target attribute is an array + * type, allow the sender to send a zero-element array (i.e. no value + * at all). XXX Need to impose a bound on the number of array + * elements. + */ + switch (wanted_type) { + case ATTR_TYPE_NUM: + if (ch != ':') { + msg_warn("missing value for number attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + number = va_arg(ap, unsigned int *); + if ((ch = attr_scan64_number(fp, number, str_buf, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + break; + case ATTR_TYPE_LONG: + if (ch != ':') { + msg_warn("missing value for number attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + long_number = va_arg(ap, unsigned long *); + if ((ch = attr_scan64_long_number(fp, long_number, str_buf, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + break; + case ATTR_TYPE_STR: + if (ch != ':') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + string = va_arg(ap, VSTRING *); + if ((ch = attr_scan64_string(fp, string, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + break; + case ATTR_TYPE_HASH: + if (ch != ':') { + msg_warn("missing value for string attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + if ((ch = attr_scan64_string(fp, str_buf, + "input attribute value")) < 0) + return (-1); + if (ch != '\n') { + msg_warn("multiple values for attribute %s from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (-1); + } + if (htable_locate(hash_table, STR(name_buf)) != 0) { + if ((flags & ATTR_FLAG_EXTRA) != 0) { + msg_warn("duplicate attribute %s in input from %s", + STR(name_buf), VSTREAM_PATH(fp)); + return (conversions); + } + } else { + htable_enter(hash_table, STR(name_buf), + mystrdup(STR(str_buf))); + } + break; + default: + msg_panic("%s: unknown type code: %d", myname, wanted_type); + } + } +} + +/* attr_scan64 - read attribute list from stream */ + +int attr_scan64(VSTREAM *fp, int flags,...) +{ + va_list ap; + int ret; + + va_start(ap, flags); + ret = attr_vscan64(fp, flags, ap); + va_end(ap); + return (ret); +} + +#ifdef TEST + + /* + * Proof of concept test program. Mirror image of the attr_scan64 test + * program. + */ +#include <msg_vstream.h> + +int var_line_limit = 2048; + +int main(int unused_argc, char **used_argv) +{ + VSTRING *str_val = vstring_alloc(1); + HTABLE *table = htable_create(1); + HTABLE_INFO **ht_info_list; + HTABLE_INFO **ht; + int int_val; + long long_val; + int ret; + + msg_verbose = 1; + msg_vstream_init(used_argv[0], VSTREAM_ERR); + if ((ret = attr_scan64(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_HASH, table, + ATTR_TYPE_END)) > 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if ((ret = attr_scan64(VSTREAM_IN, + ATTR_FLAG_STRICT, + ATTR_TYPE_NUM, ATTR_NAME_NUM, &int_val, + ATTR_TYPE_LONG, ATTR_NAME_LONG, &long_val, + ATTR_TYPE_STR, ATTR_NAME_STR, str_val, + ATTR_TYPE_END)) == 3) { + vstream_printf("%s %d\n", ATTR_NAME_NUM, int_val); + vstream_printf("%s %ld\n", ATTR_NAME_LONG, long_val); + vstream_printf("%s %s\n", ATTR_NAME_STR, STR(str_val)); + ht_info_list = htable_list(table); + for (ht = ht_info_list; *ht; ht++) + vstream_printf("(hash) %s %s\n", ht[0]->key, ht[0]->value); + myfree((char *) ht_info_list); + } else { + vstream_printf("return: %d\n", ret); + } + if (vstream_fflush(VSTREAM_OUT) != 0) + msg_fatal("write error: %m"); + + vstring_free(str_val); + htable_free(table, myfree); + + return (0); +} + +#endif diff --git a/gnu/dist/postfix/src/util/attr_scan64.ref b/gnu/dist/postfix/src/util/attr_scan64.ref new file mode 100644 index 00000000000..1b3a01581cf --- /dev/null +++ b/gnu/dist/postfix/src/util/attr_scan64.ref @@ -0,0 +1,46 @@ +./attr_print64: send attr number = 4711 +./attr_print64: send attr long_number = 1234 +./attr_print64: send attr string = whoopee +./attr_print64: send attr name foo-name value foo-value +./attr_print64: send attr name bar-name value bar-value +./attr_print64: send attr number = 4711 +./attr_print64: send attr long_number = 1234 +./attr_print64: send attr string = whoopee +./attr_scan64: unknown_stream: wanted attribute: number +./attr_scan64: input attribute name: number +./attr_scan64: input attribute value: 4711 +./attr_scan64: unknown_stream: wanted attribute: long_number +./attr_scan64: input attribute name: long_number +./attr_scan64: input attribute value: 1234 +./attr_scan64: unknown_stream: wanted attribute: string +./attr_scan64: input attribute name: string +./attr_scan64: input attribute value: whoopee +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: input attribute name: foo-name +./attr_scan64: input attribute value: foo-value +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: input attribute name: bar-name +./attr_scan64: input attribute value: bar-value +./attr_scan64: unknown_stream: wanted attribute: (any attribute name or list terminator) +./attr_scan64: input attribute name: (end) +./attr_scan64: unknown_stream: wanted attribute: number +./attr_scan64: input attribute name: number +./attr_scan64: input attribute value: 4711 +./attr_scan64: unknown_stream: wanted attribute: long_number +./attr_scan64: input attribute name: long_number +./attr_scan64: input attribute value: 1234 +./attr_scan64: unknown_stream: wanted attribute: string +./attr_scan64: input attribute name: string +./attr_scan64: input attribute value: whoopee +./attr_scan64: unknown_stream: wanted attribute: (list terminator) +./attr_scan64: input attribute name: (end) +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value +number 4711 +long_number 1234 +string whoopee +(hash) foo-name foo-value +(hash) bar-name bar-value diff --git a/gnu/dist/postfix/src/util/base64_code.c b/gnu/dist/postfix/src/util/base64_code.c new file mode 100644 index 00000000000..21210e6053d --- /dev/null +++ b/gnu/dist/postfix/src/util/base64_code.c @@ -0,0 +1,201 @@ +/*++ +/* NAME +/* base64_code 3 +/* SUMMARY +/* encode/decode data, base 64 style +/* SYNOPSIS +/* #include <base64_code.h> +/* +/* VSTRING *base64_encode(result, in, len) +/* VSTRING *result; +/* const char *in; +/* int len; +/* +/* VSTRING *base64_decode(result, in, len) +/* VSTRING *result; +/* const char *in; +/* int len; +/* DESCRIPTION +/* base64_encode() takes a block of len bytes and encodes it as one +/* null-terminated string. The result value is the result argument. +/* +/* base64_decode() performs the opposite transformation. The result +/* value is the result argument. The result is null terminated, whether +/* or not that makes sense. +/* DIAGNOSTICS +/* base64_decode () returns a null pointer when the input contains +/* characters not in the base 64 alphabet. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include <ctype.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <mymalloc.h> +#include <vstring.h> +#include <base64_code.h> + +/* Application-specific. */ + +static unsigned char to_b64[] = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +#define UNSIG_CHAR_PTR(x) ((unsigned char *)(x)) + +/* base64_encode - raw data to encoded */ + +VSTRING *base64_encode(VSTRING *result, const char *in, int len) +{ + const unsigned char *cp; + int count; + + /* + * Encode 3 -> 4. + */ + VSTRING_RESET(result); + for (cp = UNSIG_CHAR_PTR(in), count = len; count > 0; count -= 3, cp += 3) { + VSTRING_ADDCH(result, to_b64[cp[0] >> 2]); + if (count > 1) { + VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4 | cp[1] >> 4]); + if (count > 2) { + VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2 | cp[2] >> 6]); + VSTRING_ADDCH(result, to_b64[cp[2] & 0x3f]); + } else { + VSTRING_ADDCH(result, to_b64[(cp[1] & 0xf) << 2]); + VSTRING_ADDCH(result, '='); + break; + } + } else { + VSTRING_ADDCH(result, to_b64[(cp[0] & 0x3) << 4]); + VSTRING_ADDCH(result, '='); + VSTRING_ADDCH(result, '='); + break; + } + } + VSTRING_TERMINATE(result); + return (result); +} + +/* base64_decode - encoded data to raw */ + +VSTRING *base64_decode(VSTRING *result, const char *in, int len) +{ + static unsigned char *un_b64 = 0; + const unsigned char *cp; + int count; + int ch0; + int ch1; + int ch2; + int ch3; + +#define CHARS_PER_BYTE 256 +#define INVALID 0xff + + /* + * Sanity check. + */ + if (len % 4) + return (0); + + /* + * Once: initialize the decoding lookup table on the fly. + */ + if (un_b64 == 0) { + un_b64 = (unsigned char *) mymalloc(CHARS_PER_BYTE); + memset(un_b64, INVALID, CHARS_PER_BYTE); + for (cp = to_b64; cp < to_b64 + sizeof(to_b64); cp++) + un_b64[*cp] = cp - to_b64; + } + + /* + * Decode 4 -> 3. + */ + VSTRING_RESET(result); + for (cp = UNSIG_CHAR_PTR(in), count = 0; count < len; count += 4) { + if ((ch0 = un_b64[*cp++]) == INVALID + || (ch1 = un_b64[*cp++]) == INVALID) + return (0); + VSTRING_ADDCH(result, ch0 << 2 | ch1 >> 4); + if ((ch2 = *cp++) == '=') + break; + if ((ch2 = un_b64[ch2]) == INVALID) + return (0); + VSTRING_ADDCH(result, ch1 << 4 | ch2 >> 2); + if ((ch3 = *cp++) == '=') + break; + if ((ch3 = un_b64[ch3]) == INVALID) + return (0); + VSTRING_ADDCH(result, ch2 << 6 | ch3); + } + VSTRING_TERMINATE(result); + return (result); +} + +#ifdef TEST + + /* + * Proof-of-concept test program: convert to base 64 and back. + */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +int main(int unused_argc, char **unused_argv) +{ + VSTRING *b1 = vstring_alloc(1); + VSTRING *b2 = vstring_alloc(1); + char *test = "this is a test"; + +#define DECODE(b,x,l) { \ + if (base64_decode((b),(x),(l)) == 0) \ + msg_panic("bad base64: %s", (x)); \ + } +#define VERIFY(b,t) { \ + if (strcmp((b), (t)) != 0) \ + msg_panic("bad test: %s", (b)); \ + } + + base64_encode(b1, test, strlen(test)); + DECODE(b2, STR(b1), LEN(b1)); + VERIFY(STR(b2), test); + + base64_encode(b1, test, strlen(test)); + base64_encode(b2, STR(b1), LEN(b1)); + base64_encode(b1, STR(b2), LEN(b2)); + DECODE(b2, STR(b1), LEN(b1)); + DECODE(b1, STR(b2), LEN(b2)); + DECODE(b2, STR(b1), LEN(b1)); + VERIFY(STR(b2), test); + + base64_encode(b1, test, strlen(test)); + base64_encode(b2, STR(b1), LEN(b1)); + base64_encode(b1, STR(b2), LEN(b2)); + base64_encode(b2, STR(b1), LEN(b1)); + base64_encode(b1, STR(b2), LEN(b2)); + DECODE(b2, STR(b1), LEN(b1)); + DECODE(b1, STR(b2), LEN(b2)); + DECODE(b2, STR(b1), LEN(b1)); + DECODE(b1, STR(b2), LEN(b2)); + DECODE(b2, STR(b1), LEN(b1)); + VERIFY(STR(b2), test); + + vstring_free(b1); + vstring_free(b2); + return (0); +} + +#endif diff --git a/gnu/dist/postfix/src/util/base64_code.h b/gnu/dist/postfix/src/util/base64_code.h new file mode 100644 index 00000000000..d018947e1c1 --- /dev/null +++ b/gnu/dist/postfix/src/util/base64_code.h @@ -0,0 +1,36 @@ +#ifndef _BASE64_CODE_H_INCLUDED_ +#define _BASE64_CODE_H_INCLUDED_ + +/*++ +/* NAME +/* base64_code 3h +/* SUMMARY +/* encode/decode data, base 64 style +/* SYNOPSIS +/* #include <base64_code.h> +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include <vstring.h> + + /* + * External interface. + */ +extern VSTRING *base64_encode(VSTRING *, const char *, int); +extern VSTRING *base64_decode(VSTRING *, const char *, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/gnu/dist/postfix/src/util/cache.in b/gnu/dist/postfix/src/util/cache.in new file mode 100644 index 00000000000..89974bd2c66 --- /dev/null +++ b/gnu/dist/postfix/src/util/cache.in @@ -0,0 +1,26 @@ +a +1 +b +2 +c +3 +d +4 +e +5 +f +6 +f +e +d +c +b +a +1 +b +c +d +e +f +6 +f diff --git a/gnu/dist/postfix/src/util/dict.c b/gnu/dist/postfix/src/util/dict.c index 7d07d3be0be..5fd30887983 100644 --- a/gnu/dist/postfix/src/util/dict.c +++ b/gnu/dist/postfix/src/util/dict.c @@ -182,6 +182,7 @@ #include "vstring.h" #include "readlline.h" #include "mac_parse.h" +#include "stringops.h" #include "dict.h" #include "dict_ht.h" @@ -372,43 +373,18 @@ void dict_load_file(const char *dict_name, const char *path) void dict_load_fp(const char *dict_name, VSTREAM *fp) { VSTRING *buf; - char *start; char *member; char *val; - char *cp; - char *ep; int lineno; - - /* - * Ugly macros to make complex expressions less unreadable. - */ -#define SKIP(start, var, cond) \ - for (var = start; *var && (cond); var++); - -#define TRIM(s) { \ - char *p; \ - for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \ - *p = 0; \ - } + const char *err; buf = vstring_alloc(100); lineno = 0; - while (readlline(buf, fp, &lineno, READLL_STRIPNL)) { - start = STR(buf); - SKIP(start, member, ISSPACE(*member)); /* find member begin */ - if (*member == 0 || *member == '#') - continue; /* comment or blank line */ - SKIP(member, ep, !ISSPACE(*ep) && *ep != '='); /* find member end */ - SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */ - if (*cp && *cp != '=') /* need '=' or end of string */ - msg_fatal("%s, line %d: whitespace in attribute name: \"%s\"", - VSTREAM_PATH(fp), lineno, member); - if (*cp) - cp++; /* skip over '=' */ - *ep = 0; /* terminate member name */ - SKIP(cp, val, ISSPACE(*val)); /* skip leading blanks */ - TRIM(val); /* trim trailing blanks */ + while (readlline(buf, fp, &lineno)) { + if ((err = split_nameval(STR(buf), &member, &val)) != 0) + msg_fatal("%s, line %d: %s: \"%s\"", + VSTREAM_PATH(fp), lineno, err, STR(buf)); dict_update(dict_name, member, val); } vstring_free(buf); diff --git a/gnu/dist/postfix/src/util/dict.h b/gnu/dist/postfix/src/util/dict.h index 5a669aa096d..1d9d8c4a129 100644 --- a/gnu/dist/postfix/src/util/dict.h +++ b/gnu/dist/postfix/src/util/dict.h @@ -55,6 +55,7 @@ extern DICT *dict_debug(DICT *); #define DICT_FLAG_DUP_REPLACE (1<<7) /* if file, replace dups */ #define DICT_FLAG_SYNC_UPDATE (1<<8) /* if file, sync updates */ #define DICT_FLAG_DEBUG (1<<9) /* log access */ +#define DICT_FLAG_FOLD_KEY (1<<10) /* lowercase the lookup key */ extern int dict_unknown_allowed; extern int dict_errno; diff --git a/gnu/dist/postfix/src/util/dict_db.c b/gnu/dist/postfix/src/util/dict_db.c index 69518e10f8a..423244a3a7f 100644 --- a/gnu/dist/postfix/src/util/dict_db.c +++ b/gnu/dist/postfix/src/util/dict_db.c @@ -437,6 +437,23 @@ static DICT *dict_db_open(const char *class, const char *path, int open_flags, #endif + /* + * Mismatches between #include file and library are a common cause for + * trouble. + */ +#if DB_VERSION_MAJOR > 1 + int major_version; + int minor_version; + int patch_version; + + (void) db_version(&major_version, &minor_version, &patch_version); + if (major_version != DB_VERSION_MAJOR) + msg_fatal("incorrect version of Berkeley DB: " + "compiled against %d.%d.%d, linked against %d.%d.%d", + DB_VERSION_MAJOR, DB_VERSION_MINOR, DB_VERSION_PATCH, + major_version, minor_version, patch_version); +#endif + db_path = concatenate(path, ".db", (char *) 0); /* diff --git a/gnu/dist/postfix/src/util/dict_ldap.c b/gnu/dist/postfix/src/util/dict_ldap.c index ebbfe8f9003..2cf8936c43e 100644 --- a/gnu/dist/postfix/src/util/dict_ldap.c +++ b/gnu/dist/postfix/src/util/dict_ldap.c @@ -62,6 +62,9 @@ /* The cache size in bytes. Does nothing if the cache is off, of course. /* .IP \fIldapsource_\fRdereference /* How to handle LDAP aliases. See ldap.h or ldap_open(3) man page. +/* .IP \fIldapsource_\fRdebuglevel +/* Debug level. See 'loglevel' option in slapd.conf(5) man page. +/* Currently only in openldap libraries (and derivatives). /* SEE ALSO /* dict(3) generic dictionary manager /* AUTHOR(S) @@ -77,6 +80,9 @@ /* John Hensley /* john@sunislelodge.com /* +/* LaMont Jones +/* lamont@hp.com +/* /*--*/ /* System library. */ @@ -94,6 +100,14 @@ #include <ldap.h> #include <string.h> +/* Handle differences between LDAP SDK's constant definitions */ +#ifndef LDAP_CONST +#define LDAP_CONST const +#endif +#ifndef LDAP_OPT_SUCCESS +#define LDAP_OPT_SUCCESS 0 +#endif + /* Utility library. */ #include "match_list.h" @@ -131,6 +145,7 @@ typedef struct { long cache_expiry; long cache_size; int dereference; + int debuglevel; LDAP *ld; } DICT_LDAP; @@ -139,6 +154,13 @@ typedef struct { */ static jmp_buf env; +static void dict_ldap_logprint(LDAP_CONST char *data) +{ + char *myname = "dict_ldap_debug"; + + msg_info("%s: %s", myname, data); +} + static void dict_ldap_timeout(int unused_sig) { longjmp(env, 1); @@ -153,6 +175,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) #ifdef LDAP_API_FEATURE_X_MEMCACHE LDAPMemCache *dircache; + #endif #ifdef LDAP_OPT_NETWORK_TIMEOUT @@ -170,14 +193,16 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) dict_ldap->ld = ldap_init(dict_ldap->server_host, (int) dict_ldap->server_port); if (dict_ldap->ld == NULL) { - msg_warn("%s: Unable to int LDAP server %s", + msg_warn("%s: Unable to init LDAP server %s", myname, dict_ldap->server_host); dict_errno = DICT_ERR_RETRY; return (-1); } mytimeval.tv_sec = dict_ldap->timeout; mytimeval.tv_usec = 0; - ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval); + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_NETWORK_TIMEOUT, &mytimeval) != + LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to set network timeout.", myname); #else if ((saved_alarm = signal(SIGALRM, dict_ldap_timeout)) == SIG_ERR) { msg_warn("%s: Error setting signal handler for open timeout: %m", @@ -212,11 +237,24 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) * Mattice for this, and to Hery Rakotoarisoa for the v3 update. */ #if (LDAP_API_VERSION >= 2000) - ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, &(dict_ldap->dereference)); + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEREF, + &(dict_ldap->dereference)) != LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to set dereference option.", myname); #else dict_ldap->ld->ld_deref = dict_ldap->dereference; #endif +#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) + if (dict_ldap->debuglevel > 0 && + ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN, + (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS) + msg_warn("%s: Unable to set ber logprint function.", myname); + if (ldap_set_option(dict_ldap->ld, LDAP_OPT_DEBUG_LEVEL, + &(dict_ldap->debuglevel)) != LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to set LDAP debug level.", myname); +#endif + + /* * If this server requires a bind, do so. Thanks to Sam Tardieu for * noticing that the original bind call was broken. @@ -267,7 +305,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap) } else { if (msg_verbose) msg_info("%s: Caching enabled for %s", - myname, dict_ldap->ldapsource); + myname, dict_ldap->ldapsource); } } #else @@ -342,7 +380,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, if (strcasecmp(dict_ldap->result_attributes->argv[i], attr) == 0) { if (msg_verbose) - msg_info("%s: search returned %d value(s) for requested result attribute %s", myname, i, attr); + msg_info("%s: search returned %ld value(s) for requested result attribute %s", myname, i, attr); break; } } @@ -374,21 +412,23 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res, 0, &tv, &resloop); } switch (rc) { - case LDAP_SUCCESS: - dict_ldap_get_values(dict_ldap, resloop, result); - break; - case LDAP_NO_SUCH_OBJECT: - /* Go ahead and treat this as though the DN existed - * and just didn't have any result attributes. - */ - msg_warn("%s: DN %s not found, skipping ", myname, - vals[i]); - break; - default: - msg_warn("%s: search error %d: %s ", myname, rc, + case LDAP_SUCCESS: + dict_ldap_get_values(dict_ldap, resloop, result); + break; + case LDAP_NO_SUCH_OBJECT: + + /* + * Go ahead and treat this as though the DN existed + * and just didn't have any result attributes. + */ + msg_warn("%s: DN %s not found, skipping ", myname, + vals[i]); + break; + default: + msg_warn("%s: search error %d: %s ", myname, rc, ldap_err2string(rc)); - dict_errno = DICT_ERR_RETRY; - break; + dict_errno = DICT_ERR_RETRY; + break; } if (resloop != 0) @@ -428,11 +468,12 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) * load on the LDAP server. */ if (dict_ldap->domain) { - const char *p=strrchr(name,'@'); + const char *p = strrchr(name, '@'); + if (p != 0) - p=p+1; + p = p + 1; else - p=name; + p = name; if (match_list_match(dict_ldap->domain, p) == 0) { if (msg_verbose) msg_info("%s: domain of %s not found in domain list", myname, @@ -454,7 +495,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) if (dict_ldap->ld == NULL) { if (msg_verbose) msg_info - ("%s: No existing connection for ldapsource %s, reopening", + ("%s: No existing connection for LDAP source %s, reopening", myname, dict_ldap->ldapsource); dict_ldap_connect(dict_ldap); @@ -465,7 +506,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) if (dict_errno) return (0); } else if (msg_verbose) - msg_info("%s: Using existing connection for ldapsource %s", + msg_info("%s: Using existing connection for LDAP source %s", myname, dict_ldap->ldapsource); @@ -540,31 +581,32 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) /* * Make sure it's %[sud] and not something else. For backward - * compatibilty, treat anything other than %u or %d as %s, with - * a warning. + * compatibilty, treat anything other than %u or %d as %s, with a + * warning. */ if (*(sub) == '%') { - char *u=vstring_str(escaped_name); - char *p=strchr(u,'@'); - switch (*(sub+1)) { - case 'd': - if (p) - vstring_strcat(filter_buf, p+1); - break; - case 'u': - if (p) - vstring_strncat(filter_buf, u, p-u); - else - vstring_strcat(filter_buf, u); - break; - default: - msg_warn - ("%s: Invalid lookup substitution format '%%%c'!", - myname, *(sub + 1)); - /* fall through */ - case 's': + char *u = vstring_str(escaped_name); + char *p = strchr(u, '@'); + + switch (*(sub + 1)) { + case 'd': + if (p) + vstring_strcat(filter_buf, p + 1); + break; + case 'u': + if (p) + vstring_strncat(filter_buf, u, p - u); + else vstring_strcat(filter_buf, u); - break; + break; + default: + msg_warn + ("%s: Invalid lookup substitution format '%%%c'!", + myname, *(sub + 1)); + /* fall through */ + case 's': + vstring_strcat(filter_buf, u); + break; } sub++; } else @@ -580,11 +622,35 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) msg_info("%s: Searching with filter %s", myname, vstring_str(filter_buf)); - if ((rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base, - dict_ldap->scope, - vstring_str(filter_buf), - dict_ldap->result_attributes->argv, - 0, &tv, &res)) == LDAP_SUCCESS) { + rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base, + dict_ldap->scope, + vstring_str(filter_buf), + dict_ldap->result_attributes->argv, + 0, &tv, &res); + + if (rc == LDAP_SERVER_DOWN) { + if (msg_verbose) + msg_info("%s: Lost connection for LDAP source %s, reopening", + myname, dict_ldap->ldapsource); + + ldap_unbind(dict_ldap->ld); + dict_ldap->ld = NULL; + dict_ldap_connect(dict_ldap); + + /* + * if dict_ldap_connect() set dict_errno, abort. + */ + if (dict_errno) + return (0); + + rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base, + dict_ldap->scope, + vstring_str(filter_buf), + dict_ldap->result_attributes->argv, + 0, &tv, &res); + + } + if (rc == LDAP_SUCCESS) { /* * Search worked; extract the requested result_attribute. @@ -598,7 +664,9 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) */ #if (LDAP_API_VERSION >= 2000) - ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc); + if (ldap_get_option(dict_ldap->ld, LDAP_OPT_ERROR_NUMBER, &rc) != + LDAP_OPT_SUCCESS) + msg_warn("%s: Unable to get last error number.", myname); if (rc != LDAP_SUCCESS && rc != LDAP_DECODING_ERROR) msg_warn("%s: Had some trouble with entries returned by search: %s", myname, ldap_err2string(rc)); #else @@ -641,7 +709,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name) ldap_msgfree(res); if (filter_buf != 0) vstring_free(filter_buf); - if (escaped_name != 0) + if (escaped_name != 0) vstring_free(escaped_name); /* @@ -759,7 +827,12 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) mystrdup((char *) get_mail_conf_str(vstring_str(config_param), "", 0, 0)); if (*domainlist) { +#ifdef MATCH_FLAG_NONE + dict_ldap->domain = match_list_init(MATCH_FLAG_NONE, + domainlist, 1, match_string); +#else dict_ldap->domain = match_list_init(domainlist, 1, match_string); +#endif if (dict_ldap->domain == NULL) msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it", myname, domainlist); if (msg_verbose) @@ -892,6 +965,18 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) msg_info("%s: %s is %d", myname, vstring_str(config_param), dict_ldap->dereference); + /* + * Debug level. + */ +#if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN) + vstring_sprintf(config_param, "%s_debuglevel", ldapsource); + dict_ldap->debuglevel = get_mail_conf_int(vstring_str(config_param), 0, 0, + 0); + if (msg_verbose) + msg_info("%s: %s is %d", myname, vstring_str(config_param), + dict_ldap->debuglevel); +#endif + dict_ldap_connect(dict_ldap); /* @@ -908,7 +993,7 @@ DICT *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags) /* * Otherwise, we're all set. Return the new dict_ldap structure. */ - return (DICT_DEBUG(&dict_ldap->dict)); + return (DICT_DEBUG (&dict_ldap->dict)); } #endif diff --git a/gnu/dist/postfix/src/util/dict_mysql.c b/gnu/dist/postfix/src/util/dict_mysql.c index 28fc7015639..f778e928d08 100644 --- a/gnu/dist/postfix/src/util/dict_mysql.c +++ b/gnu/dist/postfix/src/util/dict_mysql.c @@ -274,14 +274,14 @@ static MYSQL_RES *plmysql_query(PLMYSQL *PLDB, if (res == 0 && host->stat == STATACTIVE) { if (!(mysql_query(host->db, query))) { if ((res = mysql_store_result(host->db)) == 0) { - msg_warn("%s", mysql_error(host->db)); + msg_warn("mysql query failed: %s", mysql_error(host->db)); plmysql_down_host(host); } else { if (msg_verbose) msg_info("dict_mysql: successful query from host %s", host->hostname); } } else { - msg_warn("%s", mysql_error(host->db)); + msg_warn("mysql query failed: %s", mysql_error(host->db)); plmysql_down_host(host); } } @@ -323,7 +323,8 @@ static void plmysql_connect_single(HOST *host, char *dbname, char *username, cha host->hostname); host->stat = STATACTIVE; } else { - msg_warn("%s", mysql_error(host->db)); + msg_warn("connect to mysql server %s: %s", + host->hostname, mysql_error(host->db)); plmysql_down_host(host); } if (hostname) @@ -366,7 +367,7 @@ DICT *dict_mysql_open(const char *name, int unused_open_flags, int dict_flags) if (dict_mysql->pldb == NULL) msg_fatal("couldn't intialize pldb!\n"); dict_register(name, (DICT *) dict_mysql); - return (DICT_DEBUG(&dict_mysql->dict)); + return (DICT_DEBUG (&dict_mysql->dict)); } /* mysqlname_parse - parse mysql configuration file */ diff --git a/gnu/dist/postfix/src/util/dict_open.c b/gnu/dist/postfix/src/util/dict_open.c index 2c15ac897ef..52a5dddc14b 100644 --- a/gnu/dist/postfix/src/util/dict_open.c +++ b/gnu/dist/postfix/src/util/dict_open.c @@ -81,6 +81,8 @@ /* .IP DICT_FLAG_SYNC_UPDATE /* With file-based maps, flush I/O buffers to file after each update. /* Thus feature is not supported with some file-based dictionaries. +/* .IP DICT_FLAG_FOLD_KEY +/* Fold the lookup key to lower case. /* .PP /* The dictionary types are as follows: /* .IP environ @@ -166,6 +168,7 @@ #include <dict_mysql.h> #include <dict_pcre.h> #include <dict_regexp.h> +#include <dict_static.h> #include <stringops.h> #include <split_at.h> #include <htable.h> @@ -212,6 +215,7 @@ static DICT_OPEN_INFO dict_open_info[] = { #ifdef HAS_POSIX_REGEXP DICT_TYPE_REGEXP, dict_regexp_open, #endif + DICT_TYPE_STATIC, dict_static_open, 0, }; @@ -339,6 +343,7 @@ static NORETURN usage(char *myname) int main(int argc, char **argv) { VSTRING *keybuf = vstring_alloc(1); + VSTRING *inbuf = vstring_alloc(1); DICT *dict; char *dict_name; int open_flags; @@ -374,13 +379,16 @@ int main(int argc, char **argv) dict_name = argv[optind]; dict = dict_open(dict_name, open_flags, DICT_FLAG_LOCK); dict_register(dict_name, dict); - while (vstring_fgets_nonl(keybuf, VSTREAM_IN)) { - bufp = vstring_str(keybuf); - if ((cmd = mystrtok(&bufp, " ")) == 0) + while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { + bufp = vstring_str(inbuf); + if ((cmd = mystrtok(&bufp, " ")) == 0 || *bufp == 0) { + vstream_printf("usage: del key|get key|put key=value\n"); + vstream_fflush(VSTREAM_OUT); continue; + } if (dict_changed()) msg_warn("dictionary has changed"); - key = mystrtok(&bufp, " ="); + key = vstring_str(unescape(keybuf, mystrtok(&bufp, " ="))); value = mystrtok(&bufp, " ="); if (strcmp(cmd, "del") == 0 && key && !value) { if (dict_del(dict, key)) @@ -404,6 +412,7 @@ int main(int argc, char **argv) vstream_fflush(VSTREAM_OUT); } vstring_free(keybuf); + vstring_free(inbuf); dict_close(dict); return (0); } diff --git a/gnu/dist/postfix/src/util/dict_pcre.c b/gnu/dist/postfix/src/util/dict_pcre.c index 6d3c6f52393..1cc0f19f946 100644 --- a/gnu/dist/postfix/src/util/dict_pcre.c +++ b/gnu/dist/postfix/src/util/dict_pcre.c @@ -257,13 +257,7 @@ DICT *dict_pcre_open(const char *map, int unused_flags, int dict_flags) if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) { msg_fatal("open %s: %m", map); } - while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) { - - if (*vstring_str(line_buffer) == '#') /* Skip comments */ - continue; - - if (*vstring_str(line_buffer) == 0) /* Skip blank lines */ - continue; + while (readlline(line_buffer, map_fp, &lineno)) { p = vstring_str(line_buffer); trimblanks(p, 0)[0] = 0; /* Trim space at end */ diff --git a/gnu/dist/postfix/src/util/dict_regexp.c b/gnu/dist/postfix/src/util/dict_regexp.c index 1debbe96ff2..732208ca67d 100644 --- a/gnu/dist/postfix/src/util/dict_regexp.c +++ b/gnu/dist/postfix/src/util/dict_regexp.c @@ -356,15 +356,9 @@ DICT *dict_regexp_open(const char *map, int unused_flags, int dict_flags) if ((map_fp = vstream_fopen(map, O_RDONLY, 0)) == 0) { msg_fatal("open %s: %m", map); } - while (readlline(line_buffer, map_fp, &lineno, READLL_STRIPNL)) { + while (readlline(line_buffer, map_fp, &lineno)) { p = vstring_str(line_buffer); - if (*p == '#') /* Skip comments */ - continue; - - if (*p == 0) /* Skip blank lines */ - continue; - trimblanks(p, 0)[0] = 0; /* Trim space at end */ rule = dict_regexp_parseline(lineno, p, &nsub, map_fp); diff --git a/gnu/dist/postfix/src/util/dict_static.c b/gnu/dist/postfix/src/util/dict_static.c new file mode 100644 index 00000000000..1b06a89aba4 --- /dev/null +++ b/gnu/dist/postfix/src/util/dict_static.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* dict_static 3 +/* SUMMARY +/* dictionary manager interface to static variables +/* SYNOPSIS +/* #include <dict_static.h> +/* +/* DICT *dict_static_open(name, dummy, dict_flags) +/* const char *name; +/* int dummy; +/* int dict_flags; +/* DESCRIPTION +/* dict_static_open() implements a dummy dictionary that returns +/* as lookup result the dictionary name, regardless of the lookup +/* key value. +/* +/* The \fIdummy\fR argument is ignored. +/* SEE ALSO +/* dict(3) generic dictionary manager +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* jeffm +/* ghostgun.com +/*--*/ + +/* System library. */ + +#include "sys_defs.h" +#include <stdio.h> /* sprintf() prototype */ +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +/* Utility library. */ + +#include "mymalloc.h" +#include "msg.h" +#include "dict.h" +#include "dict_static.h" + +/* dict_static_lookup - access static value*/ + +static const char *dict_static_lookup(DICT *dict, const char *name) +{ + dict_errno = 0; + + return (dict->name); +} + +/* dict_static_close - close static dictionary */ + +static void dict_static_close(DICT *dict) +{ + dict_free(dict); +} + +/* dict_static_open - make association with static variable */ + +DICT *dict_static_open(const char *name, int unused_flags, int dict_flags) +{ + DICT *dict; + + dict = dict_alloc(DICT_TYPE_STATIC, name, sizeof(*dict)); + dict->lookup = dict_static_lookup; + dict->close = dict_static_close; + dict->flags = dict_flags | DICT_FLAG_FIXED; + return (DICT_DEBUG (dict)); +} diff --git a/gnu/dist/postfix/src/util/dict_static.h b/gnu/dist/postfix/src/util/dict_static.h new file mode 100644 index 00000000000..d4ad1cc11d3 --- /dev/null +++ b/gnu/dist/postfix/src/util/dict_static.h @@ -0,0 +1,35 @@ +#ifndef _DICT_STATIC_H_INCLUDED_ +#define _DICT_STATIC_H_INCLUDED_ + +/*++ +/* NAME +/* dict_static 3h +/* SUMMARY +/* dictionary manager interface to static settings +/* SYNOPSIS +/* #include <dict_static.h> +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include <dict.h> + + /* + * External interface. + */ +#define DICT_TYPE_STATIC "static" + +extern DICT *dict_static_open(const char *, int, int); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* jeffm +/* ghostgun.com +/*--*/ + +#endif diff --git a/gnu/dist/postfix/src/util/dir_forest.c b/gnu/dist/postfix/src/util/dir_forest.c index 289cd122e10..929f937c730 100644 --- a/gnu/dist/postfix/src/util/dir_forest.c +++ b/gnu/dist/postfix/src/util/dir_forest.c @@ -104,7 +104,7 @@ char *dir_forest(VSTRING *buf, const char *path, int depth) } VSTRING_TERMINATE(buf); - if (msg_verbose) + if (msg_verbose > 1) msg_info("%s: %s -> %s", myname, path, vstring_str(buf)); return (vstring_str(buf)); } diff --git a/gnu/dist/postfix/src/util/events.c b/gnu/dist/postfix/src/util/events.c index c07504a26d6..99e1269565c 100644 --- a/gnu/dist/postfix/src/util/events.c +++ b/gnu/dist/postfix/src/util/events.c @@ -32,6 +32,8 @@ /* /* void event_disable_readwrite(fd) /* int fd; +/* +/* void event_drain() /* DESCRIPTION /* This module delivers I/O and timer events. /* Multiple I/O streams and timers can be monitored simultaneously. @@ -98,6 +100,10 @@ /* event_disable_readwrite() disables further I/O events on the specified /* I/O channel. The application is allowed to cancel non-existing /* I/O event requests. +/* +/* event_drain() repeatedly calls event_loop() until no more timer +/* events or I/O events are pending. This routine must not be called +/* from an event_whatever() callback routine. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory, /* system call failure. Warnings: the number of available @@ -242,6 +248,21 @@ time_t event_time(void) return (event_present); } +/* event_drain - loop until all pending events are done */ + +void event_drain(void) +{ + fd_set zero_mask; + + if (EVENT_INIT_NEEDED()) + return; + + FD_ZERO(&zero_mask); + while (event_timer_head.pred != event_timer_head.succ + || memcmp(&zero_mask, &event_xmask, sizeof(zero_mask)) != 0) + event_loop(-1); +} + /* event_enable_read - enable read events */ void event_enable_read(int fd, EVENT_NOTIFY_RDWR callback, char *context) diff --git a/gnu/dist/postfix/src/util/events.h b/gnu/dist/postfix/src/util/events.h index bc39b4787c4..d7235093b21 100644 --- a/gnu/dist/postfix/src/util/events.h +++ b/gnu/dist/postfix/src/util/events.h @@ -29,6 +29,7 @@ extern void event_disable_readwrite(int); extern time_t event_request_timer(EVENT_NOTIFY_TIME, char *, int); extern int event_cancel_timer(EVENT_NOTIFY_TIME, char *); extern void event_loop(int); +extern void event_drain(void); /* * Event codes. diff --git a/gnu/dist/postfix/src/util/fifo_trigger.c b/gnu/dist/postfix/src/util/fifo_trigger.c index b2801143610..841011a1bfc 100644 --- a/gnu/dist/postfix/src/util/fifo_trigger.c +++ b/gnu/dist/postfix/src/util/fifo_trigger.c @@ -13,7 +13,8 @@ /* int timeout; /* DESCRIPTION /* fifo_trigger() wakes up the named fifo server by writing -/* the contents of the specified buffer to the fifo. +/* the contents of the specified buffer to the fifo. There is +/* no guarantee that the written data will actually be received. /* /* Arguments: /* .IP service @@ -26,7 +27,7 @@ /* Deadline in seconds. Specify a value <= 0 to disable /* the time limit. /* DIAGNOSTICS -/* The result is zero in case of success, -1 in case of problems. +/* The result is zero when the fifo could be opened, -1 otherwise. /* BUGS /* LICENSE /* .ad @@ -49,25 +50,37 @@ #include <msg.h> #include <iostuff.h> +#include <safe_open.h> #include <trigger.h> /* fifo_trigger - wakeup fifo server */ int fifo_trigger(const char *service, const char *buf, int len, int timeout) { + static VSTRING *why; char *myname = "fifo_trigger"; + VSTREAM *fp; int fd; + if (why == 0) + why = vstring_alloc(1); + /* * Write the request to the service fifo. According to POSIX, the open * shall always return immediately, and shall return an error when no * process is reading from the FIFO. + * + * Use safe_open() so that we don't follow symlinks, and so that we don't + * open files with multiple hard links. We're not (yet) going to bother + * the caller with safe_open() specific quirks such as the why argument. */ - if ((fd = open(service, O_WRONLY | O_NONBLOCK, 0)) < 0) { + if ((fp = safe_open(service, O_WRONLY | O_NONBLOCK, 0, + (struct stat *) 0, -1, -1, why)) == 0) { if (msg_verbose) - msg_info("%s: open %s: %m", myname, service); + msg_info("%s: open %s: %s", myname, service, vstring_str(why)); return (-1); } + fd = vstream_fileno(fp); /* * Write the request... @@ -80,7 +93,7 @@ int fifo_trigger(const char *service, const char *buf, int len, int timeout) /* * Disconnect. */ - if (close(fd)) + if (vstream_fclose(fp)) if (msg_verbose) msg_warn("%s: close %s: %m", myname, service); return (0); diff --git a/gnu/dist/postfix/src/util/inet_trigger.c b/gnu/dist/postfix/src/util/inet_trigger.c index e92c2d8dbfb..dddbcb62aac 100644 --- a/gnu/dist/postfix/src/util/inet_trigger.c +++ b/gnu/dist/postfix/src/util/inet_trigger.c @@ -16,6 +16,10 @@ /* a brief connection to it and by writing the contents of the /* named buffer. /* +/* The connection is closed by a background thread. Some kernels +/* cannot handle client-side disconnect before the server has +/* received the message. +/* /* Arguments: /* .IP service /* Name of the communication endpoint. @@ -45,6 +49,7 @@ /* System library. */ #include <sys_defs.h> +#include <sys/socket.h> #include <unistd.h> #include <string.h> @@ -53,13 +58,42 @@ #include <msg.h> #include <connect.h> #include <iostuff.h> +#include <mymalloc.h> +#include <events.h> #include <trigger.h> +struct inet_trigger { + int fd; + char *service; +}; + +/* inet_trigger_event - disconnect from peer */ + +static void inet_trigger_event(int event, char *context) +{ + struct inet_trigger *ip = (struct inet_trigger *) context; + static char *myname = "inet_trigger_event"; + + /* + * Disconnect. + */ + if (event == EVENT_TIME) + msg_warn("%s: read timeout for service %s", myname, ip->service); + event_disable_readwrite(ip->fd); + event_cancel_timer(inet_trigger_event, context); + if (close(ip->fd) < 0) + msg_warn("%s: close %s: %m", myname, ip->service); + myfree(ip->service); + myfree((char *) ip); +} + + /* inet_trigger - wakeup INET-domain server */ int inet_trigger(const char *service, const char *buf, int len, int timeout) { char *myname = "inet_trigger"; + struct inet_trigger *ip; int fd; if (msg_verbose > 1) @@ -73,19 +107,24 @@ int inet_trigger(const char *service, const char *buf, int len, int timeout) msg_warn("%s: connect to %s: %m", myname, service); return (-1); } + close_on_exec(fd, CLOSE_ON_EXEC); + ip = (struct inet_trigger *) mymalloc(sizeof(*ip)); + ip->fd = fd; + ip->service = mystrdup(service); /* * Write the request... */ - if (write_buf(fd, buf, len, timeout) < 0) + if (write_buf(fd, buf, len, timeout) < 0 + || write_buf(fd, "", 1, timeout) < 0) if (msg_verbose) msg_warn("%s: write to %s: %m", myname, service); /* - * Disconnect. + * Wakeup when the peer disconnects, or when we lose patience. */ - if (close(fd) < 0) - if (msg_verbose) - msg_warn("%s: close %s: %m", myname, service); + if (timeout > 0) + event_request_timer(inet_trigger_event, (char *) ip, timeout + 100); + event_enable_read(fd, inet_trigger_event, (char *) ip); return (0); } diff --git a/gnu/dist/postfix/src/util/intv.c b/gnu/dist/postfix/src/util/intv.c new file mode 100644 index 00000000000..1d117fe0209 --- /dev/null +++ b/gnu/dist/postfix/src/util/intv.c @@ -0,0 +1,114 @@ +/*++ +/* NAME +/* intv 3 +/* SUMMARY +/* integer array utilities +/* SYNOPSIS +/* #include <intv.h> +/* +/* INTV *intv_alloc(len) +/* int len; +/* +/* INTV *intv_free(intvp) +/* INTV *intvp; +/* +/* void intv_add(intvp, count, arg, ...) +/* INTV *intvp; +/* int count; +/* int *arg; +/* DESCRIPTION +/* The functions in this module manipulate arrays of integers. +/* An INTV structure contains the following members: +/* .IP len +/* The actual length of the \fIintv\fR array. +/* .IP intc +/* The number of \fIintv\fR elements used. +/* .IP intv +/* An array of integer values. +/* .PP +/* intv_alloc() returns an empty integer array of the requested +/* length. The result is ready for use by intv_add(). +/* +/* intv_add() copies zero or more integers and adds them to the +/* specified integer array. +/* +/* intv_free() releases storage for an integer array, and conveniently +/* returns a null pointer. +/* SEE ALSO +/* msg(3) diagnostics interface +/* DIAGNOSTICS +/* Fatal errors: memory allocation problem. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include <sys_defs.h> +#include <stdlib.h> /* 44BSD stdarg.h uses abort() */ +#include <stdarg.h> + +/* Application-specific. */ + +#include "mymalloc.h" +#include "msg.h" +#include "intv.h" + +/* intv_free - destroy integer array */ + +INTV *intv_free(INTV *intvp) +{ + myfree((char *) intvp->intv); + myfree((char *) intvp); + return (0); +} + +/* intv_alloc - initialize integer array */ + +INTV *intv_alloc(int len) +{ + INTV *intvp; + + /* + * Sanity check. + */ + if (len < 1) + msg_panic("intv_alloc: bad array length %d", len); + + /* + * Initialize. + */ + intvp = (INTV *) mymalloc(sizeof(*intvp)); + intvp->len = len; + intvp->intv = (int *) mymalloc(intvp->len * sizeof(intvp->intv[0])); + intvp->intc = 0; + return (intvp); +} + +/* intv_add - add integer to vector */ + +void intv_add(INTV *intvp, int count,...) +{ + va_list ap; + + /* + * Make sure that always intvp->intc < intvp->len. + */ + va_start(ap, count); + while (count-- > 0) { + if (intvp->intc >= intvp->len) { + intvp->len *= 2; + intvp->intv = (int *) myrealloc((char *) intvp->intv, + intvp->len * sizeof(int)); + } + intvp->intv[intvp->intc++] = va_arg(ap, int); + } + va_end(ap); +} diff --git a/gnu/dist/postfix/src/util/intv.h b/gnu/dist/postfix/src/util/intv.h new file mode 100644 index 00000000000..d84c5b4b191 --- /dev/null +++ b/gnu/dist/postfix/src/util/intv.h @@ -0,0 +1,38 @@ +#ifndef _INTV_H_INCLUDED_ +#define _INTV_H_INCLUDED_ + +/*++ +/* NAME +/* intv 3h +/* SUMMARY +/* string array utilities +/* SYNOPSIS +/* #include "intv.h" + DESCRIPTION + .nf + + /* + * External interface. + */ +typedef struct INTV { + int len; /* number of array elements */ + int intc; /* array elements in use */ + int *intv; /* integer array */ +} INTV; + +extern INTV *intv_alloc(int); +extern void intv_add(INTV *, int,...); +extern INTV *intv_free(INTV *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/gnu/dist/postfix/src/util/make_dirs.c b/gnu/dist/postfix/src/util/make_dirs.c index b3efcddbf60..ad1b451de70 100644 --- a/gnu/dist/postfix/src/util/make_dirs.c +++ b/gnu/dist/postfix/src/util/make_dirs.c @@ -54,6 +54,7 @@ int make_dirs(const char *path, int perms) int saved_ch; struct stat st; int ret; + mode_t saved_mode = 0; /* * Initialize. Make a copy of the path that we can safely clobber. @@ -78,14 +79,31 @@ int make_dirs(const char *path, int perms) ret = -1; break; } + saved_mode = st.st_mode; } else { if (errno != ENOENT) break; + + /* + * Create a new directory. Unfortunately, mkdir(2) has no + * equivalent of open(2)'s O_CREAT|O_EXCL safety net, so we must + * require that the parent directory is not world writable. + * Detecting a lost race condition after the fact is not + * sufficient, as an attacker could repeat the attack and add one + * directory level at a time. + */ + if (saved_mode & S_IWOTH) { + msg_warn("refusing to mkdir %s: parent directory is writable by everyone", + saved_path); + errno = EPERM; + ret = -1; + break; + } if ((ret = mkdir(saved_path, perms)) < 0) { if (errno != EEXIST) break; /* Race condition? */ - if ((ret = stat(saved_path, &st)) < 0) + if ((ret = stat(saved_path, &st)) < 0) break; if (!S_ISDIR(st.st_mode)) { errno = ENOTDIR; diff --git a/gnu/dist/postfix/src/util/match_list.h b/gnu/dist/postfix/src/util/match_list.h index aba88b8f694..910ae713bf2 100644 --- a/gnu/dist/postfix/src/util/match_list.h +++ b/gnu/dist/postfix/src/util/match_list.h @@ -15,9 +15,9 @@ * External interface. */ typedef struct MATCH_LIST MATCH_LIST; -typedef int (*MATCH_LIST_FN) (const char *, const char *); +typedef int (*MATCH_LIST_FN) (int, const char *, const char *); -extern MATCH_LIST *match_list_init(const char *, int,...); +extern MATCH_LIST *match_list_init(int, const char *, int,...); extern int match_list_match(MATCH_LIST *,...); extern void match_list_free(MATCH_LIST *); diff --git a/gnu/dist/postfix/src/util/match_ops.h b/gnu/dist/postfix/src/util/match_ops.h index a938c04fb34..e147f7b50dd 100644 --- a/gnu/dist/postfix/src/util/match_ops.h +++ b/gnu/dist/postfix/src/util/match_ops.h @@ -13,9 +13,13 @@ /* External interface. */ -extern int match_string(const char *, const char *); -extern int match_hostname(const char *, const char *); -extern int match_hostaddr(const char *, const char *); +#define MATCH_FLAG_NONE 0 +#define MATCH_FLAG_PARENT (1<<0) +#define MATCH_FLAG_ALL (MATCH_FLAG_PARENT) + +extern int match_string(int, const char *, const char *); +extern int match_hostname(int, const char *, const char *); +extern int match_hostaddr(int, const char *, const char *); /* LICENSE /* .ad diff --git a/gnu/dist/postfix/src/util/msg.c b/gnu/dist/postfix/src/util/msg.c index 08379a90b86..683359b2b37 100644 --- a/gnu/dist/postfix/src/util/msg.c +++ b/gnu/dist/postfix/src/util/msg.c @@ -20,6 +20,10 @@ /* NORETURN msg_fatal(format, ...) /* const char *format; /* +/* NORETURN msg_fatal_status(status, format, ...) +/* int status; +/* const char *format; +/* /* NORETURN msg_panic(format, ...) /* const char *format; /* @@ -48,11 +52,14 @@ /* msg_fatal() reports an unrecoverable error and terminates the program /* with a non-zero exit status. /* +/* msg_fatal_status() reports an unrecoverable error and terminates the +/* program with the specified exit status. +/* /* msg_panic() reports an internal inconsistency, terminates the /* program immediately (i.e. without calling the optional user-specified /* cleanup routine), and forces a core dump when possible. /* -/* msg_cleanup() specifies a function that msg_fatal() should +/* msg_cleanup() specifies a function that msg_fatal[_status]() should /* invoke before terminating the program, and returns the /* current function pointer. Specify a null argument to disable /* this feature. @@ -163,6 +170,23 @@ NORETURN msg_fatal(const char *fmt,...) exit(1); } +/* msg_fatal_status - report error and terminate gracefully */ + +NORETURN msg_fatal_status(int status, const char *fmt,...) +{ + va_list ap; + + if (msg_exiting++ == 0) { + va_start(ap, fmt); + msg_vprintf(MSG_FATAL, fmt, ap); + va_end(ap); + if (msg_cleanup_fn) + msg_cleanup_fn(); + } + sleep(1); + exit(status); +} + /* msg_panic - report error and dump core */ NORETURN msg_panic(const char *fmt,...) diff --git a/gnu/dist/postfix/src/util/msg.h b/gnu/dist/postfix/src/util/msg.h index fae8a2327c9..42198a2a71b 100644 --- a/gnu/dist/postfix/src/util/msg.h +++ b/gnu/dist/postfix/src/util/msg.h @@ -22,6 +22,7 @@ extern void PRINTFLIKE(1, 2) msg_info(const char *,...); extern void PRINTFLIKE(1, 2) msg_warn(const char *,...); extern void PRINTFLIKE(1, 2) msg_error(const char *,...); extern NORETURN PRINTFLIKE(1, 2) msg_fatal(const char *,...); +extern NORETURN PRINTFLIKE(2, 3) msg_fatal_status(int, const char *,...); extern NORETURN PRINTFLIKE(1, 2) msg_panic(const char *,...); extern int msg_error_limit(int); diff --git a/gnu/dist/postfix/src/util/mymalloc.c b/gnu/dist/postfix/src/util/mymalloc.c index e20531c0605..61014a800e0 100644 --- a/gnu/dist/postfix/src/util/mymalloc.c +++ b/gnu/dist/postfix/src/util/mymalloc.c @@ -90,7 +90,10 @@ typedef struct MBLOCK { int signature; /* set when block is active */ int length; /* user requested length */ - char payload[1]; /* actually a bunch of bytes */ + union { + ALIGN_TYPE align; + char payload[1]; /* actually a bunch of bytes */ + } u; } MBLOCK; #define SIGNATURE 0xdead @@ -99,7 +102,7 @@ typedef struct MBLOCK { #define CHECK_IN_PTR(ptr, real_ptr, len, fname) { \ if (ptr == 0) \ msg_panic("%s: null pointer input", fname); \ - real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, payload[0])); \ + real_ptr = (MBLOCK *) (ptr - offsetof(MBLOCK, u.payload[0])); \ if (real_ptr->signature != SIGNATURE) \ msg_panic("%s: corrupt or unallocated memory block", fname); \ real_ptr->signature = 0; \ @@ -110,10 +113,10 @@ typedef struct MBLOCK { #define CHECK_OUT_PTR(ptr, real_ptr, len) { \ real_ptr->signature = SIGNATURE; \ real_ptr->length = len; \ - ptr = real_ptr->payload; \ + ptr = real_ptr->u.payload; \ } -#define SPACE_FOR(len) (offsetof(MBLOCK, payload[0]) + len) +#define SPACE_FOR(len) (offsetof(MBLOCK, u.payload[0]) + len) /* mymalloc - allocate memory or bust */ @@ -165,6 +168,8 @@ void myfree(char *ptr) char *mystrdup(const char *str) { + if (str == 0) + msg_panic("mystrdup: null pointer argument"); return (strcpy(mymalloc(strlen(str) + 1), str)); } @@ -173,10 +178,12 @@ char *mystrdup(const char *str) char *mystrndup(const char *str, int len) { char *result; - int slen; + char *cp; - if ((slen = strlen(str)) < len) - len = slen; + if (str == 0) + msg_panic("mystrndup: null pointer argument"); + if ((cp = memchr(str, 0, len)) != 0) + len = cp - str; result = memcpy(mymalloc(len + 1), str, len); result[len] = 0; return (result); @@ -186,5 +193,7 @@ char *mystrndup(const char *str, int len) char *mymemdup(const char *ptr, int len) { + if (ptr == 0) + msg_panic("mymemdup: null pointer argument"); return (memcpy(mymalloc(len), ptr, len)); } diff --git a/gnu/dist/postfix/src/util/myrand.c b/gnu/dist/postfix/src/util/myrand.c new file mode 100644 index 00000000000..39fcbfd9e47 --- /dev/null +++ b/gnu/dist/postfix/src/util/myrand.c @@ -0,0 +1,63 @@ +/*++ +/* NAME +/* myrand 3 +/* SUMMARY +/* rand wrapper +/* SYNOPSIS +/* #include <myrand.h> +/* +/* void mysrand(seed) +/* int seed; +/* +/* int myrand() +/* DESCRIPTION +/* This module implements a wrapper for the portable, pseudo-random +/* number generator. The wrapper adds automatic initialization. +/* +/* mysrand() performs initialization. This call may be skipped. +/* +/* myrand() returns a pseudo-random number in the range [0, RAND_MAX]. +/* If mysrand() was not called, it is invoked with the process ID +/* ex-or-ed with the time of day in seconds. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* WARNING +/* Do not use this code for generating unpredictable numbers. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> + +/* Utility library. */ + +#include <myrand.h> + +static int myrand_initdone = 0; + +/* mysrand - initialize */ + +void mysrand(int seed) +{ + srand(seed); + myrand_initdone = 1; +} + +/* myrand - pseudo-random number */ + +int myrand(void) +{ + if (myrand_initdone == 0) + mysrand(getpid() ^ time((time_t *) 0)); + return (rand()); +} diff --git a/gnu/dist/postfix/src/util/myrand.h b/gnu/dist/postfix/src/util/myrand.h new file mode 100644 index 00000000000..49c5abb8433 --- /dev/null +++ b/gnu/dist/postfix/src/util/myrand.h @@ -0,0 +1,31 @@ +#ifndef _MYRAND_H_INCLUDED_ +#define _MYRAND_H_INCLUDED_ + +/*++ +/* NAME +/* myrand 3h +/* SUMMARY +/* rand wrapper +/* SYNOPSIS +/* #include <myrand.h> +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +extern void mysrand(int); +extern int myrand(void); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/gnu/dist/postfix/src/util/netstring.c b/gnu/dist/postfix/src/util/netstring.c new file mode 100644 index 00000000000..3f3e73112ad --- /dev/null +++ b/gnu/dist/postfix/src/util/netstring.c @@ -0,0 +1,351 @@ +/*++ +/* NAME +/* netstring 3 +/* SUMMARY +/* netstring stream I/O support +/* SYNOPSIS +/* #include <netstring.h> +/* +/* void netstring_setup(stream, timeout) +/* VSTREAM *stream; +/* int timeout; +/* +/* void netstring_except(stream, exception) +/* VSTREAM *stream; +/* int exception; +/* +/* VSTRING *netstring_get(stream, buf, limit) +/* VSTREAM *stream; +/* VSTRING *buf; +/* int limit; +/* +/* void netstring_put(stream, data, len) +/* VSTREAM *stream; +/* const char *data; +/* int len; +/* +/* void netstring_put_multi(stream, data, len, data, len, ..., 0) +/* VSTREAM *stream; +/* const char *data; +/* int len; +/* +/* void NETSTRING_PUT_BUF(stream, buf) +/* VSTREAM *stream; +/* VSTRING *buf; +/* +/* void netstring_fflush(stream) +/* VSTREAM *stream; +/* +/* VSTRING *netstring_memcpy(buf, data, len) +/* VSTRING *buf; +/* const char *data; +/* int len; +/* +/* VSTRING *netstring_memcat(buf, data, len) +/* VSTRING *buf; +/* const char *src; +/* int len; +/* AUXILIARY ROUTINES +/* int netstring_get_length(stream) +/* VSTREAM *stream; +/* +/* VSTRING *netstring_get_data(stream, buf, len) +/* VSTREAM *stream; +/* VSTRING *buf; +/* int len; +/* +/* void netstring_get_terminator(stream) +/* VSTREAM *stream; +/* DESCRIPTION +/* This module reads and writes netstrings with error detection: +/* timeouts, unexpected end-of-file, or format errors. Netstring +/* is a data format designed by Daniel Bernstein. +/* +/* netstring_setup() arranges for a time limit on the netstring +/* read and write operations described below. +/* This routine alters the behavior of streams as follows: +/* .IP \(bu +/* The read/write timeout is set to the specified value. +/* .IP \(bu +/* The stream is configured to enable exception handling. +/* .PP +/* netstring_except() raises the specified exception on the +/* named stream. See the DIAGNOSTICS section below. +/* +/* netstring_get() reads a netstring from the specified stream +/* and extracts its content. The limit specifies a maximal size. +/* Specify zero to disable the size limit. The result is not null +/* terminated. The result value is the buf argument. +/* +/* netstring_put() encapsulates the specified string as a netstring +/* and sends the result to the specified stream. +/* The stream output buffer is not flushed. +/* +/* netstring_put_multi() encapsulates the content of multiple strings +/* as one netstring and sends the result to the specified stream. The +/* argument list must be terminated with a null data pointer. +/* The stream output buffer is not flushed. +/* +/* NETSTRING_PUT_BUF() is a macro that provides a VSTRING-based +/* wrapper for the netstring_put() routine. +/* +/* netstring_fflush() flushes the output buffer of the specified +/* stream and handles any errors. +/* +/* netstring_memcpy() encapsulates the specified data as a netstring +/* and copies the result over the specified buffer. The result +/* value is the buffer. +/* +/* netstring_memcat() encapsulates the specified data as a netstring +/* and appends the result to the specified buffer. The result +/* value is the buffer. +/* +/* The following routines provide low-level access to a netstring +/* stream. +/* +/* netstring_get_length() reads a length field from the specified +/* stream, and absorbs the netstring length field terminator. +/* +/* netstring_get_data() reads the specified number of bytes from the +/* specified stream into the specified buffer, and absorbs the +/* netstring terminator. The result value is the buf argument. +/* +/* netstring_get_terminator() reads the netstring terminator from +/* the specified stream. +/* DIAGNOSTICS +/* .fi +/* .ad +/* In case of error, a vstream_longjmp() call is performed to the +/* context specified with vstream_setjmp(). +/* Error codes passed along with vstream_longjmp() are: +/* .IP NETSTRING_ERR_EOF +/* An I/O error happened, or the peer has disconnected unexpectedly. +/* .IP NETSTRING_ERR_TIME +/* The time limit specified to netstring_setup() was exceeded. +/* .IP NETSTRING_ERR_FORMAT +/* The input contains an unexpected character value. +/* .IP NETSTRING_ERR_SIZE +/* The input is larger than acceptable. +/* BUGS +/* The timeout deadline affects all I/O on the named stream, not +/* just the I/O done on behalf of this module. +/* +/* The timeout deadline overwrites any previously set up state on +/* the named stream. +/* +/* netstrings are not null terminated, which makes printing them +/* a bit awkward. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* SEE ALSO +/* http://cr.yp.to/proto/netstrings.txt, netstring definition +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <stdarg.h> +#include <ctype.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> +#include <netstring.h> + +/* Application-specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* netstring_setup - initialize netstring stream */ + +void netstring_setup(VSTREAM *stream, int timeout) +{ + vstream_control(stream, + VSTREAM_CTL_TIMEOUT, timeout, + VSTREAM_CTL_EXCEPT, + VSTREAM_CTL_END); +} + +/* netstring_except - process netstring stream exception */ + +void netstring_except(VSTREAM *stream, int exception) +{ + vstream_longjmp(stream, exception); +} + +/* netstring_get_length - read netstring length + terminator */ + +int netstring_get_length(VSTREAM *stream) +{ + char *myname = "netstring_get_length"; + int len = 0; + int ch; + + for (;;) { + switch (ch = VSTREAM_GETC(stream)) { + case VSTREAM_EOF: + netstring_except(stream, vstream_ftimeout(stream) ? + NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); + case ':': + if (msg_verbose > 1) + msg_info("%s: read netstring length %d", myname, len); + return (len); + default: + if (!ISDIGIT(ch)) + netstring_except(stream, NETSTRING_ERR_FORMAT); + len = len * 10 + ch - '0'; + break; + } + } +} + +/* netstring_get_data - read netstring payload + terminator */ + +VSTRING *netstring_get_data(VSTREAM *stream, VSTRING *buf, int len) +{ + char *myname = "netstring_get_data"; + + /* + * Allocate buffer space. + */ + VSTRING_RESET(buf); + VSTRING_SPACE(buf, len); + + /* + * Read the payload and absorb the terminator. + */ + if (vstream_fread(stream, STR(buf), len) != len) + netstring_except(stream, vstream_ftimeout(stream) ? + NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); + if (msg_verbose > 1) + msg_info("%s: read netstring data %.*s", + myname, len < 30 ? len : 30, STR(buf)); + netstring_get_terminator(stream); + + /* + * Position the buffer. + */ + VSTRING_AT_OFFSET(buf, len); + return (buf); +} + +/* netstring_get_terminator - absorb netstring terminator */ + +void netstring_get_terminator(VSTREAM *stream) +{ + if (VSTREAM_GETC(stream) != ',') + netstring_except(stream, NETSTRING_ERR_FORMAT); +} + +/* netstring_get - read string from netstring stream */ + +VSTRING *netstring_get(VSTREAM *stream, VSTRING *buf, int limit) +{ + int len; + + len = netstring_get_length(stream); + if (limit && len > limit) + netstring_except(stream, NETSTRING_ERR_SIZE); + netstring_get_data(stream, buf, len); + return (buf); +} + +/* netstring_put - send string as netstring */ + +void netstring_put(VSTREAM *stream, const char *data, int len) +{ + char *myname = "netstring_put"; + + if (msg_verbose > 1) + msg_info("%s: write netstring len %d data %.*s", + myname, len, len < 30 ? len : 30, data); + vstream_fprintf(stream, "%d:", len); + vstream_fwrite(stream, data, len); + VSTREAM_PUTC(',', stream); +} + +/* netstring_put_multi - send multiple strings as one netstring */ + +void netstring_put_multi(VSTREAM *stream,...) +{ + char *myname = "netstring_put_multi"; + int total; + char *data; + int data_len; + va_list ap; + + /* + * Figure out the total result size. + */ + va_start(ap, stream); + for (total = 0; (data = va_arg(ap, char *)) != 0; total += data_len) + if ((data_len = va_arg(ap, int)) < 0) + msg_panic("netstring_put_multi: bad data length %d", data_len); + va_end(ap); + + /* + * Debugging support. + */ + if (msg_verbose > 1) { + va_start(ap, stream); + data = va_arg(ap, char *); + data_len = va_arg(ap, int); + msg_info("%s: write netstring len %d data %.*s", + myname, total, data_len < 30 ? data_len : 30, data); + va_end(ap); + } + + /* + * Send the length, content and terminator. + */ + vstream_fprintf(stream, "%d:", total); + va_start(ap, stream); + while ((data = va_arg(ap, char *)) != 0) { + data_len = va_arg(ap, int); + if (data_len > 0) + if (vstream_fwrite(stream, data, data_len) != data_len) + netstring_except(stream, vstream_ftimeout(stream) ? + NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); + va_end(ap); + } + vstream_fwrite(stream, ",", 1); +} + +/* netstring_fflush - flush netstring stream */ + +void netstring_fflush(VSTREAM *stream) +{ + if (vstream_fflush(stream) == VSTREAM_EOF) + netstring_except(stream, vstream_ftimeout(stream) ? + NETSTRING_ERR_TIME : NETSTRING_ERR_EOF); +} + +/* netstring_memcpy - copy data as in-memory netstring */ + +VSTRING *netstring_memcpy(VSTRING *buf, const char *src, int len) +{ + vstring_sprintf(buf, "%d:", len); + vstring_memcat(buf, src, len); + VSTRING_ADDCH(buf, ','); + return (buf); +} + +/* netstring_memcat - append data as in-memory netstring */ + +VSTRING *netstring_memcat(VSTRING *buf, const char *src, int len) +{ + vstring_sprintf_append(buf, "%d:", len); + vstring_memcat(buf, src, len); + VSTRING_ADDCH(buf, ','); + return (buf); +} diff --git a/gnu/dist/postfix/src/util/netstring.h b/gnu/dist/postfix/src/util/netstring.h new file mode 100644 index 00000000000..6b82d90e4e8 --- /dev/null +++ b/gnu/dist/postfix/src/util/netstring.h @@ -0,0 +1,54 @@ +#ifndef _NETSTRING_H_INCLUDED_ +#define _NETSTRING_H_INCLUDED_ + +/*++ +/* NAME +/* netstring 3h +/* SUMMARY +/* netstring stream I/O support +/* SYNOPSIS +/* #include <netstring.h> +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include <vstring.h> +#include <vstream.h> + + /* + * External interface. + */ +#define NETSTRING_ERR_EOF 1 /* unexpected disconnect */ +#define NETSTRING_ERR_TIME 2 /* time out */ +#define NETSTRING_ERR_FORMAT 3 /* format error */ +#define NETSTRING_ERR_SIZE 4 /* netstring too large */ + +extern void netstring_except(VSTREAM *, int); +extern void netstring_setup(VSTREAM *, int); +extern int netstring_get_length(VSTREAM *); +extern VSTRING *netstring_get_data(VSTREAM *, VSTRING *, int); +extern void netstring_get_terminator(VSTREAM *); +extern VSTRING *netstring_get(VSTREAM *, VSTRING *, int); +extern void netstring_put(VSTREAM *, const char *, int); +extern void netstring_put_multi(VSTREAM *,...); +extern void netstring_fflush(VSTREAM *); +extern VSTRING *netstring_memcpy(VSTRING *, const char *, int); +extern VSTRING *netstring_memcat(VSTRING *, const char *, int); + +#define NETSTRING_PUT_BUF(str, buf) \ + netstring_put((str), vstring_str(buf), VSTRING_LEN(buf)) + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#endif diff --git a/gnu/dist/postfix/src/util/rand_sleep.c b/gnu/dist/postfix/src/util/rand_sleep.c index d119f074f57..33ffecb51c4 100644 --- a/gnu/dist/postfix/src/util/rand_sleep.c +++ b/gnu/dist/postfix/src/util/rand_sleep.c @@ -45,6 +45,7 @@ /* Utility library. */ #include <msg.h> +#include <myrand.h> #include <iostuff.h> /* rand_sleep - block for random time */ @@ -52,7 +53,6 @@ void rand_sleep(unsigned delay, unsigned variation) { char *myname = "rand_sleep"; - static pid_t my_pid; unsigned usec; /* @@ -66,9 +66,7 @@ void rand_sleep(unsigned delay, unsigned variation) /* * Use the semi-crappy random number generator. */ - if (my_pid == 0) - srand((my_pid = getpid()) ^ time((time_t *) 0)); - usec = (delay - variation / 2) + variation * (double) rand() / RAND_MAX; + usec = (delay - variation / 2) + variation * (double) myrand() / RAND_MAX; doze(usec); } diff --git a/gnu/dist/postfix/src/util/readlline.c b/gnu/dist/postfix/src/util/readlline.c index 314d6962bf0..9be31eab109 100644 --- a/gnu/dist/postfix/src/util/readlline.c +++ b/gnu/dist/postfix/src/util/readlline.c @@ -6,30 +6,37 @@ /* SYNOPSIS /* #include <readlline.h> /* -/* VSTRING *readlline(buf, fp, lineno, stripnl) +/* VSTRING *readlline(buf, fp, lineno) /* VSTRING *buf; /* VSTREAM *fp; /* int *lineno; -/* int stripnl; /* DESCRIPTION /* readlline() reads one logical line from the named stream. -/* A line that starts with whitespace is a continuation of -/* the previous line. When the stripnl argument is non-zero, -/* the newline between continued lines -/* is deleted from the input. The result value is the input -/* buffer argument or a null pointer when no input is found. +/* .IP "blank lines and comments" +/* Empty lines and whitespace-only lines are ignored, as +/* are lines whose first non-whitespace character is a `#'. +/* .IP "multi-line text" +/* A logical line starts with non-whitespace text. A line that +/* starts with whitespace continues a logical line. +/* .PP +/* The result value is the input buffer argument or a null pointer +/* when no input is found. /* /* Arguments: /* .IP buf -/* A variable-length buffer for input. +/* A variable-length buffer for input. The result is null terminated. /* .IP fp /* Handle to an open stream. /* .IP lineno /* A null pointer, or a pointer to an integer that is incremented -/* after reading a newline. -/* .IP stripnl -/* Non-zero to strip newlines. readlline.h provides the symbolic -/* constants READLL_STRIPNL and READLL_KEEPNL for convenience. +/* after reading a newline character. +/* .RE +/* DIAGNOSTICS +/* Warning: a continuation line that does not continue preceding text. +/* The invalid input is ignored, to avoid complicating caller code. +/* SECURITY +/* readlline() imposes no logical line length limit therefore it +/* should be used for reading trusted information only. /* LICENSE /* .ad /* .fi @@ -44,40 +51,73 @@ /* System library. */ #include <sys_defs.h> +#include <ctype.h> /* Utility library. */ +#include "msg.h" #include "vstream.h" #include "vstring.h" #include "readlline.h" +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) +#define END(x) vstring_end(x) + /* readlline - read one logical line */ -VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno, int stripnl) +VSTRING *readlline(VSTRING *buf, VSTREAM *fp, int *lineno) { int ch; int next; + int start; + char *cp; + + VSTRING_RESET(buf); /* - * Lines that start with whitespace continue the preceding line. + * Ignore comment lines, all whitespace lines, and empty lines. Terminate + * at EOF or at the beginning of the next logical line. */ - VSTRING_RESET(buf); - while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF) { - if (ch == '\n') { - if (stripnl == 0) - VSTRING_ADDCH(buf, ch); - if (lineno) - *lineno += 1; - if ((next = VSTREAM_GETC(fp)) == ' ' || next == '\t') { - ch = next; - } else { - if (next != VSTREAM_EOF) - vstream_ungetc(fp, next); + for (;;) { + /* Read one line, possibly not newline terminated. */ + start = LEN(buf); + while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') + VSTRING_ADDCH(buf, ch); + if (ch == '\n' && lineno != 0) + *lineno += 1; + /* Ignore comment line, all whitespace line, or empty line. */ + for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++) + /* void */ ; + if (cp == END(buf) || *cp == '#') + vstring_truncate(buf, start); + /* Terminate at EOF or at the beginning of the next logical line. */ + if (ch == VSTREAM_EOF) + break; + if (LEN(buf) > 0) { + if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF) + vstream_ungetc(fp, next); + if (next != '#' && !ISSPACE(next)) break; - } } - VSTRING_ADDCH(buf, ch); } + + /* + * Invalid input: continuing text without preceding text. Allowing this + * would complicate "postconf -e", which implements its own multi-line + * parsing routine. Do not abort, just warn, so that critical programs + * like postmap do not leave behind a truncated table. + */ + if (LEN(buf) > 0 && ISSPACE(*STR(buf))) { + msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"", + VSTREAM_PATH(fp), STR(buf), + LEN(buf) > 30 ? "..." : ""); + return (readlline(buf, fp, lineno)); + } + + /* + * Done. + */ VSTRING_TERMINATE(buf); - return (VSTRING_LEN(buf) || ch == '\n' ? buf : 0); + return (LEN(buf) > 0 ? buf : 0); } diff --git a/gnu/dist/postfix/src/util/readlline.h b/gnu/dist/postfix/src/util/readlline.h index 00658433cc2..8a8bd548711 100644 --- a/gnu/dist/postfix/src/util/readlline.h +++ b/gnu/dist/postfix/src/util/readlline.h @@ -20,10 +20,7 @@ /* * External interface. */ -extern VSTRING *readlline(VSTRING *, VSTREAM *, int *, int); - -#define READLL_STRIPNL 1 -#define READLL_KEEPNL 0 +extern VSTRING *readlline(VSTRING *, VSTREAM *, int *); /* LICENSE /* .ad diff --git a/gnu/dist/postfix/src/util/safe_open.c b/gnu/dist/postfix/src/util/safe_open.c index e1e88f8b475..d20c9404c1a 100644 --- a/gnu/dist/postfix/src/util/safe_open.c +++ b/gnu/dist/postfix/src/util/safe_open.c @@ -41,8 +41,9 @@ /* Panic: interface violations. /* /* A null result means there was a problem. The nature of the -/* problem is returned via the \fIwhy\fR buffer; some errors -/* cannot be reported via \fIerrno\fR. +/* problem is returned via the \fIwhy\fR buffer; when an error +/* cannot be reported via \fIerrno\fR, the generic value EPERM +/* (operation not permitted) is used instead. /* HISTORY /* .fi /* .ad @@ -54,7 +55,7 @@ /* be fooled by delaying the open() until the inode found with /* lstat() has been re-used for a sensitive file (article /* <20000103212443.A5807@monad.swb.de> posted to bugtraq on -/* Jan 3, 2000). This can be a concern for a set-uid process +/* Jan 3, 2000). This can be a concern for a set-ugid process /* that runs under the control of a user and that can be /* manipulated with start/stop signals. /* LICENSE @@ -113,8 +114,10 @@ static VSTREAM *safe_open_exist(const char *path, int flags, } else if (fstat_st->st_nlink != 1) { vstring_sprintf(why, "file has %d hard links", (int) fstat_st->st_nlink); + errno = EPERM; } else if (S_ISDIR(fstat_st->st_mode)) { vstring_sprintf(why, "file is a directory"); + errno = EISDIR; } /* @@ -135,10 +138,12 @@ static VSTREAM *safe_open_exist(const char *path, int flags, */ else if (lstat(path, &lstat_st) < 0) { vstring_sprintf(why, "file status changed unexpectedly: %m"); + errno = EPERM; } else if (S_ISLNK(lstat_st.st_mode)) { if (lstat_st.st_uid == 0) return (fp); vstring_sprintf(why, "file is a symbolic link"); + errno = EPERM; } else if (fstat_st->st_dev != lstat_st.st_dev || fstat_st->st_ino != lstat_st.st_ino #ifdef HAS_ST_GEN @@ -147,6 +152,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags, || fstat_st->st_nlink != lstat_st.st_nlink || fstat_st->st_mode != lstat_st.st_mode) { vstring_sprintf(why, "file status changed unexpectedly"); + errno = EPERM; } /* @@ -158,9 +164,7 @@ static VSTREAM *safe_open_exist(const char *path, int flags, /* * End up here in case of fstat()/lstat() problems or inconsistencies. - * Reset errno to reduce confusion. */ - errno = 0; vstream_fclose(fp); return (0); } @@ -190,7 +194,7 @@ static VSTREAM *safe_open_create(const char *path, int flags, int mode, if (CHANGE_OWNER(user, group) && fchown(vstream_fileno(fp), user, group) < 0) { - vstring_sprintf(why, "cannot change file ownership: %m"); + msg_warn("%s: cannot change file ownership: %m", path); } /* diff --git a/gnu/dist/postfix/src/util/sane_accept.c b/gnu/dist/postfix/src/util/sane_accept.c index d9d8464d0ad..9ae9f6e3f49 100644 --- a/gnu/dist/postfix/src/util/sane_accept.c +++ b/gnu/dist/postfix/src/util/sane_accept.c @@ -36,6 +36,7 @@ /* Utility library. */ +#include "msg.h" #include "sane_accept.h" /* sane_accept - sanitize accept() error returns */ @@ -55,6 +56,10 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) EWOULDBLOCK, 0, }; + static int accept_warn_errors[] = { + ECONNABORTED, + 0, + }; int count; int err; int fd; @@ -68,6 +73,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) * XXX LINUX < 2.1 accept() wakes up before the three-way handshake is * complete, so it can fail with ECONNRESET and other "false alarm" * indications. + * + * XXX FreeBSD 4.2-STABLE accept() returns ECONNABORTED when a UNIX-domain + * client has disconnected in the mean time. The data that was sent with + * connect() write() close() is lost, even though the write() and close() + * reported successful completion. This was fixed shortly before FreeBSD + * 4.3. However, other systems may make that same mistake again, so we're + * adding a special warning. */ if ((fd = accept(sock, sa, len)) < 0) { for (count = 0; (err = accept_ok_errors[count]) != 0; count++) { @@ -76,6 +88,13 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len) break; } } + for (count = 0; (err = accept_warn_errors[count]) != 0; count++) { + if (errno == err) { + msg_warn("accept: %m"); + errno = EAGAIN; + break; + } + } } return (fd); } diff --git a/gnu/dist/postfix/src/util/split_nameval.c b/gnu/dist/postfix/src/util/split_nameval.c new file mode 100644 index 00000000000..f3e519bad1e --- /dev/null +++ b/gnu/dist/postfix/src/util/split_nameval.c @@ -0,0 +1,94 @@ +/*++ +/* NAME +/* split_nameval 3 +/* SUMMARY +/* name-value splitter +/* SYNOPSIS +/* #include <split_nameval.h> +/* +/* const char *split_nameval(buf, name, value) +/* char *buf; +/* char **name; +/* char **value; +/* DESCRIPTION +/* split_nameval() takes a logical line from readlline() and expects +/* text of the form "name = value" or "name =". The buffer +/* argument is broken up into name and value substrings. +/* +/* Arguments: +/* .IP buf +/* Result from readlline() or equivalent. The buffer is modified. +/* .IP name +/* Upon successful completion, this is set to the name +/* substring. +/* .IP value +/* Upon successful completion, this is set to the value +/* substring. +/* FEATURES +/* SEE ALSO +/* dict(3) mid-level dictionary routines +/* BUGS +/* DIAGNOSTICS +/* Fatal errors: out of memory. +/* +/* The result is a null pointer in case of success, a string +/* describing the error otherwise: missing '=' after attribute +/* name; missing attribute name. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System libraries. */ + +#include "sys_defs.h" +#include <ctype.h> +#include <string.h> + +/* Utility library. */ + +#include <msg.h> +#include <stringops.h> + +/* split_nameval - split text into name and value */ + +const char *split_nameval(char *buf, char **name, char **value) +{ + char *np; /* name substring */ + char *vp; /* value substring */ + char *cp; + char *ep; + + /* + * Ugly macros to make complex expressions less unreadable. + */ +#define SKIP(start, var, cond) \ + for (var = start; *var && (cond); var++); + +#define TRIM(s) { \ + char *p; \ + for (p = (s) + strlen(s); p > (s) && ISSPACE(p[-1]); p--); \ + *p = 0; \ + } + + SKIP(buf, np, ISSPACE(*np)); /* find name begin */ + if (*np == 0) + return ("missing attribute name"); + SKIP(np, ep, !ISSPACE(*ep) && *ep != '='); /* find name end */ + SKIP(ep, cp, ISSPACE(*cp)); /* skip blanks before '=' */ + if (*cp != '=') /* need '=' */ + return ("missing '=' after attribute name"); + *ep = 0; /* terminate name */ + cp++; /* skip over '=' */ + SKIP(cp, vp, ISSPACE(*vp)); /* skip leading blanks */ + TRIM(vp); /* trim trailing blanks */ + *name = np; + *value = vp; + return (0); +} diff --git a/gnu/dist/postfix/src/util/stream_trigger.c b/gnu/dist/postfix/src/util/stream_trigger.c index 585f392bf6e..9cb57e5135e 100644 --- a/gnu/dist/postfix/src/util/stream_trigger.c +++ b/gnu/dist/postfix/src/util/stream_trigger.c @@ -15,6 +15,10 @@ /* stream_trigger() wakes up the named stream server by making /* a brief connection to it and writing the named buffer. /* +/* The connection is closed by a background thread. Some kernels +/* cannot handle client-side disconnect before the server has +/* received the message. +/* /* Arguments: /* .IP service /* Name of the communication endpoint. @@ -51,13 +55,41 @@ #include <msg.h> #include <connect.h> #include <iostuff.h> +#include <mymalloc.h> +#include <events.h> #include <trigger.h> +struct stream_trigger { + int fd; + char *service; +}; + +/* stream_trigger_event - disconnect from peer */ + +static void stream_trigger_event(int event, char *context) +{ + struct stream_trigger *sp = (struct stream_trigger *) context; + static char *myname = "stream_trigger_event"; + + /* + * Disconnect. + */ + if (event == EVENT_TIME) + msg_warn("%s: read timeout for service %s", myname, sp->service); + event_disable_readwrite(sp->fd); + event_cancel_timer(stream_trigger_event, context); + if (close(sp->fd) < 0) + msg_warn("%s: close %s: %m", myname, sp->service); + myfree(sp->service); + myfree((char *) sp); +} + /* stream_trigger - wakeup stream server */ int stream_trigger(const char *service, const char *buf, int len, int timeout) { char *myname = "stream_trigger"; + struct stream_trigger *sp; int fd; if (msg_verbose > 1) @@ -71,19 +103,28 @@ int stream_trigger(const char *service, const char *buf, int len, int timeou msg_warn("%s: connect to %s: %m", myname, service); return (-1); } + close_on_exec(fd, CLOSE_ON_EXEC); + + /* + * Stash away context. + */ + sp = (struct stream_trigger *) mymalloc(sizeof(*sp)); + sp->fd = fd; + sp->service = mystrdup(service); /* * Write the request... */ - if (write_buf(fd, buf, len, timeout) < 0) + if (write_buf(fd, buf, len, timeout) < 0 + || write_buf(fd, "", 1, timeout) < 0) if (msg_verbose) msg_warn("%s: write to %s: %m", myname, service); /* - * Disconnect. + * Wakeup when the peer disconnects, or when we lose patience. */ - if (close(fd) < 0) - if (msg_verbose) - msg_warn("%s: close %s: %m", myname, service); + if (timeout > 0) + event_request_timer(stream_trigger_event, (char *) sp, timeout + 100); + event_enable_read(fd, stream_trigger_event, (char *) sp); return (0); } diff --git a/gnu/dist/postfix/src/util/stringops.h b/gnu/dist/postfix/src/util/stringops.h index 1310d4cb062..0cd80c797d1 100644 --- a/gnu/dist/postfix/src/util/stringops.h +++ b/gnu/dist/postfix/src/util/stringops.h @@ -30,6 +30,8 @@ extern char *translit(char *, const char *, const char *); extern char *basename(const char *); #endif extern VSTRING *unescape(VSTRING *, const char *); +extern int alldig(const char *); +extern const char *split_nameval(char *, char **, char **); /* LICENSE /* .ad diff --git a/gnu/dist/postfix/src/util/unix_trigger.c b/gnu/dist/postfix/src/util/unix_trigger.c index d0f03d6c78f..6a3b1916073 100644 --- a/gnu/dist/postfix/src/util/unix_trigger.c +++ b/gnu/dist/postfix/src/util/unix_trigger.c @@ -15,6 +15,10 @@ /* unix_trigger() wakes up the named UNIX-domain server by making /* a brief connection to it and writing the named buffer. /* +/* The connection is closed by a background thread. Some kernels +/* cannot handle client-side disconnect before the server has +/* received the message. +/* /* Arguments: /* .IP service /* Name of the communication endpoint. @@ -43,6 +47,7 @@ /* System library. */ #include <sys_defs.h> +#include <sys/socket.h> #include <unistd.h> #include <string.h> @@ -51,13 +56,41 @@ #include <msg.h> #include <connect.h> #include <iostuff.h> +#include <mymalloc.h> +#include <events.h> #include <trigger.h> +struct unix_trigger { + int fd; + char *service; +}; + +/* unix_trigger_event - disconnect from peer */ + +static void unix_trigger_event(int event, char *context) +{ + struct unix_trigger *up = (struct unix_trigger *) context; + static char *myname = "unix_trigger_event"; + + /* + * Disconnect. + */ + if (event == EVENT_TIME) + msg_warn("%s: read timeout for service %s", myname, up->service); + event_disable_readwrite(up->fd); + event_cancel_timer(unix_trigger_event, context); + if (close(up->fd) < 0) + msg_warn("%s: close %s: %m", myname, up->service); + myfree(up->service); + myfree((char *) up); +} + /* unix_trigger - wakeup UNIX-domain server */ int unix_trigger(const char *service, const char *buf, int len, int timeout) { char *myname = "unix_trigger"; + struct unix_trigger *up; int fd; if (msg_verbose > 1) @@ -71,19 +104,28 @@ int unix_trigger(const char *service, const char *buf, int len, int timeout) msg_warn("%s: connect to %s: %m", myname, service); return (-1); } + close_on_exec(fd, CLOSE_ON_EXEC); + + /* + * Stash away context. + */ + up = (struct unix_trigger *) mymalloc(sizeof(*up)); + up->fd = fd; + up->service = mystrdup(service); /* * Write the request... */ - if (write_buf(fd, buf, len, timeout) < 0) + if (write_buf(fd, buf, len, timeout) < 0 + || write_buf(fd, "", 1, timeout) < 0) if (msg_verbose) msg_warn("%s: write to %s: %m", myname, service); /* - * Disconnect. + * Wakeup when the peer disconnects, or when we lose patience. */ - if (close(fd) < 0) - if (msg_verbose) - msg_warn("%s: close %s: %m", myname, service); + if (timeout > 0) + event_request_timer(unix_trigger_event, (char *) up, timeout + 100); + event_enable_read(fd, unix_trigger_event, (char *) up); return (0); } diff --git a/gnu/dist/postfix/src/util/vstream.c b/gnu/dist/postfix/src/util/vstream.c index e2ec94ddcf7..22c59440ca7 100644 --- a/gnu/dist/postfix/src/util/vstream.c +++ b/gnu/dist/postfix/src/util/vstream.c @@ -72,6 +72,9 @@ /* int vstream_fileno(stream) /* VSTREAM *stream; /* +/* void *vstream_context(stream) +/* VSTREAM *stream; +/* /* int vstream_ferror(stream) /* VSTREAM *stream; /* @@ -97,7 +100,7 @@ /* int vstream_setjmp(stream) /* VSTREAM *stream; /* -/* void longjmp(stream, val) +/* void vstream_longjmp(stream, val) /* VSTREAM *stream; /* int val; /* @@ -175,11 +178,11 @@ /* /* VSTREAM_PUTCHAR(c) is an alias for VSTREAM_PUTC(c, VSTREAM_OUT). /* -/* vstream_unget() pushes back a character onto the specified stream +/* vstream_ungetc() pushes back a character onto the specified stream /* and returns the character, or VSTREAM_EOF in case of problems. /* It is an error to push back before reading (or immediately after /* changing the stream offset via vstream_fseek()). Upon successful -/* return, vstream_unget() clears the end-of-file stream flag. +/* return, vstream_ungetc() clears the end-of-file stream flag. /* /* vstream_fputs() appends the given null-terminated string to the /* specified buffered stream. The result is 0 in case of success, @@ -250,6 +253,9 @@ /* a buffered stream. With streams that have separate read/write /* file descriptors, the result is the current descriptor. /* +/* vstream_context() returns the application context that is passed on to +/* the application-specified read/write routines. +/* /* VSTREAM_PATH() is an unsafe macro that returns the name stored /* with vstream_fopen() or with vstream_control(). The macro is /* unsafe because it evaluates some arguments more than once. diff --git a/gnu/dist/postfix/src/util/vstream.h b/gnu/dist/postfix/src/util/vstream.h index f38259b2701..3ec6382956b 100644 --- a/gnu/dist/postfix/src/util/vstream.h +++ b/gnu/dist/postfix/src/util/vstream.h @@ -90,6 +90,7 @@ extern VSTREAM *vstream_fdopen(int, int); #define VSTREAM_GETCHAR() VSTREAM_GETC(VSTREAM_IN) #define vstream_fileno(vp) ((vp)->fd) +#define vstream_context(vp) ((vp)->context) #define vstream_ferror(vp) vbuf_error(&(vp)->buf) #define vstream_feof(vp) vbuf_eof(&(vp)->buf) #define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf) diff --git a/gnu/dist/postfix/src/util/vstring.c b/gnu/dist/postfix/src/util/vstring.c index 356b114b2b8..0f3e33b5e40 100644 --- a/gnu/dist/postfix/src/util/vstring.c +++ b/gnu/dist/postfix/src/util/vstring.c @@ -67,6 +67,16 @@ /* const char *src; /* int len; /* +/* VSTRING *vstring_memcpy(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* int len; +/* +/* VSTRING *vstring_memcat(vp, src, len) +/* VSTRING *vp; +/* const char *src; +/* int len; +/* /* VSTRING *vstring_sprintf(vp, format, ...) /* VSTRING *vp; /* const char *format; @@ -101,14 +111,15 @@ /* of at least "len" bytes. The minimal length is 1. The result /* is a null-terminated string of length zero. /* -/* vstring_ctl() gives control over memory management policy. +/* vstring_ctl() gives additional control over vstring behavior. /* The function takes a VSTRING pointer and a list of zero -/* or more (name,value) pairs. The expected valye type of the -/* value depends on the specified name. The name codes are: +/* or more (name,value) pairs. The expected value type +/* depends on the specified name. The value name codes are: /* .IP "VSTRING_CTL_MAXLEN (int)" /* Specifies a hard upper limit on a string's length. When the /* length would be exceeded, the program simulates a memory /* allocation problem (i.e. it terminates through msg_fatal()). +/* This fuctionality is currently unimplemented. /* .IP "VSTRING_CTL_END (no value)" /* Specifies the end of the argument list. Forgetting to terminate /* the argument list may cause the program to crash. @@ -174,6 +185,14 @@ /* vstring_strncat() copies at most \fIlen\fR characters. Otherwise it is /* identical to vstring_strcat(). /* +/* vstring_memcpy() copies \fIlen\fR bytes to a variable-length string. +/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is not null-terminated. +/* +/* vstring_memcat() appends \fIlen\fR bytes to a variable-length string. +/* \fIsrc\fP provides the data to be copied; \fIvp\fP is the +/* target and result value. The result is not null-terminated. +/* /* vstring_sprintf() produces a formatted string according to its /* \fIformat\fR argument. See vstring_vsprintf() for details. /* @@ -392,6 +411,29 @@ VSTRING *vstring_strncat(VSTRING *vp, const char *src, int len) return (vp); } +/* vstring_memcpy - copy buffer of limited length */ + +VSTRING *vstring_memcpy(VSTRING *vp, const char *src, int len) +{ + VSTRING_RESET(vp); + + VSTRING_SPACE(vp, len); + memcpy(vstring_str(vp), src, len); + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + +/* vstring_memcat - append buffer of limited length */ + +VSTRING *vstring_memcat(VSTRING *vp, const char *src, int len) +{ + VSTRING_SPACE(vp, len); + memcpy(vstring_end(vp), src, len); + len += VSTRING_LEN(vp); + VSTRING_AT_OFFSET(vp, len); + return (vp); +} + /* vstring_export - VSTRING to bare string */ char *vstring_export(VSTRING *vp) diff --git a/gnu/dist/postfix/src/util/vstring.h b/gnu/dist/postfix/src/util/vstring.h index 6ae8947e784..b1b81bb8acd 100644 --- a/gnu/dist/postfix/src/util/vstring.h +++ b/gnu/dist/postfix/src/util/vstring.h @@ -38,6 +38,8 @@ extern VSTRING *vstring_strcpy(VSTRING *, const char *); extern VSTRING *vstring_strncpy(VSTRING *, const char *, int); extern VSTRING *vstring_strcat(VSTRING *, const char *); extern VSTRING *vstring_strncat(VSTRING *, const char *, int); +extern VSTRING *vstring_memcpy(VSTRING *, const char *, int); +extern VSTRING *vstring_memcat(VSTRING *, const char *, int); extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf(VSTRING *, const char *,...); extern VSTRING *PRINTFLIKE(2, 3) vstring_sprintf_append(VSTRING *, const char *,...); extern char *vstring_export(VSTRING *); diff --git a/gnu/dist/postfix/src/util/vstring_vstream.c b/gnu/dist/postfix/src/util/vstring_vstream.c index 078d255e1ac..2f644bf1dc7 100644 --- a/gnu/dist/postfix/src/util/vstring_vstream.c +++ b/gnu/dist/postfix/src/util/vstring_vstream.c @@ -27,6 +27,11 @@ /* VSTRING *vp; /* VSTREAM *fp; /* int bound; +/* +/* int vstring_get_null_bound(vp, fp, bound) +/* VSTRING *vp; +/* VSTREAM *fp; +/* int bound; /* DESCRIPTION /* The routines in this module each read one newline or null-terminated /* string from an input stream. In all cases the result is either the @@ -41,7 +46,7 @@ /* vstring_get_null() reads a null-terminated string from the named /* stream. /* -/* vstring_get_bound() and vstring_get_nonl_bound() read no more +/* the vstring_get<whatever>_bound() routines read no more /* than \fIbound\fR characters. Otherwise they behave like the /* unbounded versions documented above. /* DIAGNOSTICS @@ -154,6 +159,22 @@ int vstring_get_nonl_bound(VSTRING *vp, VSTREAM *fp, int bound) return (c == '\n' ? c : VSTRING_GET_RESULT(vp)); } +/* vstring_get_null_bound - read null-terminated string from file */ + +int vstring_get_null_bound(VSTRING *vp, VSTREAM *fp, int bound) +{ + int c; + + if (bound <= 0) + msg_panic("vstring_get_nonl_bound: invalid bound %d", bound); + + VSTRING_RESET(vp); + while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0) + VSTRING_ADDCH(vp, c); + VSTRING_TERMINATE(vp); + return (c == 0 ? c : VSTRING_GET_RESULT(vp)); +} + #ifdef TEST /* diff --git a/gnu/dist/postfix/src/util/vstring_vstream.h b/gnu/dist/postfix/src/util/vstring_vstream.h index 897167ca640..68bd8106db0 100644 --- a/gnu/dist/postfix/src/util/vstring_vstream.h +++ b/gnu/dist/postfix/src/util/vstring_vstream.h @@ -24,6 +24,7 @@ extern int vstring_get_nonl(VSTRING *, VSTREAM *); extern int vstring_get_null(VSTRING *, VSTREAM *); extern int vstring_get_bound(VSTRING *, VSTREAM *, int); extern int vstring_get_nonl_bound(VSTRING *, VSTREAM *, int); +extern int vstring_get_null_bound(VSTRING *, VSTREAM *, int); /* * Backwards compatibility for code that still uses the vstring_fgets() |
