static void bounce_template_parse_buffer(BOUNCE_TEMPLATE *tp) { char *tval = tp->buffer; char *cp; char **cpp; int cpp_len; int cpp_used; int hlen; char *hval; /* * Sanity check. */ if ((tp->flags & BOUNCE_TMPL_FLAG_NEW_BUFFER) == 0) msg_panic("bounce_template_parse_buffer: nothing to do here"); tp->flags &= ~BOUNCE_TMPL_FLAG_NEW_BUFFER; /* * Discard the unusable template and use the default one instead. */ #define CLEANUP_AND_RETURN() do { \ bounce_template_reset(tp); \ return; \ } while (0) /* * Parse pseudo-header labels and values. */ #define GETLINE(line, buf) \ (((line) = (buf)) != 0 ? ((buf) = split_at((buf), '\n'), (line)) : 0) while ((GETLINE(cp, tval)) != 0 && (hlen = is_header(cp)) > 0) { for (hval = cp + hlen; *hval && (*hval == ':' || ISSPACE(*hval)); hval++) *hval = 0; if (*hval == 0) { msg_warn("%s: empty \"%s\" header value in %s template " "-- ignoring this template", tp->origin, cp, tp->class); CLEANUP_AND_RETURN(); } if (!allascii(hval)) { msg_warn("%s: non-ASCII \"%s\" header value in %s template " "-- ignoring this template", tp->origin, cp, tp->class); CLEANUP_AND_RETURN(); }
DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why) { DNS_RR *addr_list; int res_opt = 0; const char *ahost; dsb_reset(why); /* Paranoia */ if (smtp_dns_support == SMTP_DNS_DNSSEC) res_opt |= RES_USE_DNSSEC; /* * IDNA support. */ #ifndef NO_EAI if (!allascii(host) && (ahost = midna_domain_to_ascii(host)) != 0) { if (msg_verbose) msg_info("%s asciified to %s", host, ahost); } else #endif ahost = host; /* * If the host is specified by numerical address, just convert the * address to internal form. Otherwise, the host is specified by name. */ #define PREF0 0 addr_list = smtp_addr_one((DNS_RR *) 0, ahost, res_opt, PREF0, why); if (addr_list && (misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) && smtp_find_self(addr_list) != 0) { dns_rr_free(addr_list); dsb_simple(why, "5.4.6", "mail for %s loops back to myself", host); return (0); } if (addr_list && addr_list->next) { if (var_smtp_rand_addr) addr_list = dns_rr_shuffle(addr_list); /* The following changes the order of equal-preference hosts. */ if (inet_proto_info()->ai_family_list[1] != 0) addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); } if (msg_verbose) smtp_print_addr(host, addr_list); return (addr_list); }
int valid_utf8_hostname(int enable_utf8, const char *name, int gripe) { static const char myname[] = "valid_utf8_hostname"; /* * Trivial cases first. */ if (*name == 0) { if (gripe) msg_warn("%s: empty domain name", myname); return (0); } /* * Convert non-ASCII domain name to ASCII and validate the result per * STD3. midna_domain_to_ascii() applies valid_hostname() to the result. * Propagate the gripe parameter for better diagnostics (note that * midna_domain_to_ascii() logs a problem only when the result is not * cached). */ #ifndef NO_EAI if (enable_utf8 && !allascii(name)) { if (midna_domain_to_ascii(name) == 0) { if (gripe) msg_warn("%s: malformed UTF-8 domain name", myname); return (0); } else { return (1); } } #endif /* * Validate ASCII name per STD3. */ return (valid_hostname(name, gripe)); }
static int match_servername(const char *certid, const TLS_CLIENT_START_PROPS *props) { const ARGV *cmatch_argv; const char *nexthop = props->nexthop; const char *hname = props->host; const char *domain; const char *parent; const char *aname; int match_subdomain; int i; int idlen; int domlen; if ((cmatch_argv = props->matchargv) == 0) return 0; #ifndef NO_EAI /* * DNS subjectAltNames are required to be ASCII. * * Per RFC 6125 Section 6.4.4 Matching the CN-ID, follows the same rules * (6.4.1, 6.4.2 and 6.4.3) that apply to subjectAltNames. In * particular, 6.4.2 says that the reference identifier is coerced to * ASCII, but no conversion is stated or implied for the CN-ID, so it * seems it only matches if it is all ASCII. Otherwise, it is some other * sort of name. */ if (!allascii(certid)) return (0); if (!allascii(nexthop) && (aname = midna_domain_to_ascii(nexthop)) != 0) { if (msg_verbose) msg_info("%s asciified to %s", nexthop, aname); nexthop = aname; } #endif /* * Match the certid against each pattern until we find a match. */ for (i = 0; i < cmatch_argv->argc; ++i) { match_subdomain = 0; if (!strcasecmp(cmatch_argv->argv[i], "nexthop")) domain = nexthop; else if (!strcasecmp(cmatch_argv->argv[i], "hostname")) domain = hname; else if (!strcasecmp(cmatch_argv->argv[i], "dot-nexthop")) { domain = nexthop; match_subdomain = 1; } else { domain = cmatch_argv->argv[i]; if (*domain == '.') { if (domain[1]) { ++domain; match_subdomain = 1; } } #ifndef NO_EAI /* * Besides U+002E (full stop) IDNA2003 allows labels to be * separated by any of the Unicode variants U+3002 (ideographic * full stop), U+FF0E (fullwidth full stop), and U+FF61 * (halfwidth ideographic full stop). Their respective UTF-8 * encodings are: E38082, EFBC8E and EFBDA1. * * IDNA2008 does not permit (upper) case and other variant * differences in U-labels. The midna_domain_to_ascii() function, * based on UTS46, normalizes such differences away. * * The IDNA to_ASCII conversion does not allow empty leading labels, * so we handle these explicitly here. */ else { unsigned char *cp = (unsigned char *) domain; if ((cp[0] == 0xe3 && cp[1] == 0x80 && cp[2] == 0x82) || (cp[0] == 0xef && cp[1] == 0xbc && cp[2] == 0x8e) || (cp[0] == 0xef && cp[1] == 0xbd && cp[2] == 0xa1)) { if (domain[3]) { domain = domain + 3; match_subdomain = 1; } } } if (!allascii(domain) && (aname = midna_domain_to_ascii(domain)) != 0) { if (msg_verbose) msg_info("%s asciified to %s", domain, aname); domain = aname; } #endif } /* * Sub-domain match: certid is any sub-domain of hostname. */ if (match_subdomain) { if ((idlen = strlen(certid)) > (domlen = strlen(domain)) + 1 && certid[idlen - domlen - 1] == '.' && !strcasecmp(certid + (idlen - domlen), domain)) return (1); else continue; } /* * Exact match and initial "*" match. The initial "*" in a certid * matches one (if var_tls_multi_label is false) or more hostname * components under the condition that the certid contains multiple * hostname components. */ if (!strcasecmp(certid, domain) || (certid[0] == '*' && certid[1] == '.' && certid[2] != 0 && (parent = strchr(domain, '.')) != 0 && (idlen = strlen(certid + 1)) <= (domlen = strlen(parent)) && strcasecmp(var_tls_multi_wildcard == 0 ? parent : parent + domlen - idlen, certid + 1) == 0)) return (1); } return (0); }
char *casefoldx(int flags, VSTRING *dest, const char *src, ssize_t len) { size_t old_len; #ifdef NO_EAI /* * ASCII mode only. */ if (len < 0) len = strlen(src); if ((flags & CASEF_FLAG_APPEND) == 0) VSTRING_RESET(dest); old_len = VSTRING_LEN(dest); vstring_strncat(dest, src, len); lowercase(STR(dest) + old_len); return (STR(dest)); #else /* * Unicode mode. */ const char myname[] = "casefold"; static VSTRING *fold_buf = 0; static UCaseMap *csm = 0; UErrorCode error; ssize_t space_needed; int n; /* * Handle special cases. */ if (len < 0) len = strlen(src); if (dest == 0) dest = (fold_buf != 0 ? fold_buf : (fold_buf = vstring_alloc(100))); if ((flags & CASEF_FLAG_APPEND) == 0) VSTRING_RESET(dest); old_len = VSTRING_LEN(dest); /* * All-ASCII input, or ASCII mode only. */ if ((flags & CASEF_FLAG_UTF8) == 0 || allascii(src)) { vstring_strncat(dest, src, len); lowercase(STR(dest) + old_len); return (STR(dest)); } /* * ICU 4.8 ucasemap_utf8FoldCase() does not complain about UTF-8 syntax * errors. XXX Based on source-code review we conclude that non-UTF-8 * bytes are copied verbatim, and experiments confirm this. Given that * this behavior is intentional, we assume that it will stay that way. */ #if 0 if (valid_utf8_string(src, len) == 0) { if (err) *err = "malformed UTF-8 or invalid codepoint"; return (0); } #endif /* * One-time initialization. With ICU 4.8 this works while chrooted. */ if (csm == 0) { error = U_ZERO_ERROR; csm = ucasemap_open("en_US", U_FOLD_CASE_DEFAULT, &error); if (U_SUCCESS(error) == 0) msg_fatal("ucasemap_open error: %s", u_errorName(error)); } /* * Fold the input, adjusting the buffer size if needed. Safety: don't * loop forever. * * Note: the requested amount of space for casemapped output (as reported * with space_needed below) does not include storage for the null * terminator. The terminator is written only when the output buffer is * large enough. This is why we overallocate space when the output does * not fit. But if the output fits exactly, then the ouput will be * unterminated, and we have to terminate the output ourselves. */ for (n = 0; n < 3; n++) { error = U_ZERO_ERROR; space_needed = ucasemap_utf8FoldCase(csm, STR(dest) + old_len, vstring_avail(dest), src, len, &error); if (U_SUCCESS(error)) { VSTRING_AT_OFFSET(dest, old_len + space_needed); if (vstring_avail(dest) == 0) /* exact fit, no terminator */ VSTRING_TERMINATE(dest); /* add terminator */ break; } else if (error == U_BUFFER_OVERFLOW_ERROR) { VSTRING_SPACE(dest, space_needed + 1); /* for terminator */ } else { msg_fatal("%s: conversion error for \"%s\": %s", myname, src, u_errorName(error)); } } return (STR(dest)); #endif /* NO_EAI */ }
static void postmap(char *map_type, char *path_name, int postmap_flags, int open_flags, int dict_flags) { VSTREAM *NOCLOBBER source_fp; VSTRING *line_buffer; MKMAP *mkmap; int lineno; int last_line; char *key; char *value; struct stat st; mode_t saved_mask; /* * Initialize. */ line_buffer = vstring_alloc(100); if ((open_flags & O_TRUNC) == 0) { /* Incremental mode. */ source_fp = VSTREAM_IN; vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); } else { /* Create database. */ if (strcmp(map_type, DICT_TYPE_PROXY) == 0) msg_fatal("can't create maps via the proxy service"); dict_flags |= DICT_FLAG_BULK_UPDATE; if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", path_name); } if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); /* * Turn off group/other read permissions as indicated in the source file. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) saved_mask = umask(022 | (~st.st_mode & 077)); /* * If running as root, run as the owner of the source file, so that the * result shows proper ownership, and so that a bug in postmap does not * allow privilege escalation. */ if ((postmap_flags & POSTMAP_FLAG_AS_OWNER) && getuid() == 0 && (st.st_uid != geteuid() || st.st_gid != getegid())) set_eugid(st.st_uid, st.st_gid); /* * Open the database, optionally create it when it does not exist, * optionally truncate it when it does exist, and lock out any * spectators. */ mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags); /* * And restore the umask, in case it matters. */ if ((postmap_flags & POSTMAP_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) umask(saved_mask); /* * Trap "exceptions" so that we can restart a bulk-mode update after a * recoverable error. */ for (;;) { if (dict_isjmp(mkmap->dict) != 0 && dict_setjmp(mkmap->dict) != 0 && vstream_fseek(source_fp, SEEK_SET, 0) < 0) msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp)); /* * Add records to the database. */ last_line = 0; while (readllines(line_buffer, source_fp, &last_line, &lineno)) { /* * First some UTF-8 checks sans casefolding. */ if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) && !allascii(STR(line_buffer)) && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) { msg_warn("%s, line %d: non-UTF-8 input \"%s\"" " -- ignoring this line", VSTREAM_PATH(source_fp), lineno, STR(line_buffer)); continue; } /* * Split on the first whitespace character, then trim leading and * trailing whitespace from key and value. */ key = STR(line_buffer); value = key + strcspn(key, CHARS_SPACE); if (*value) *value++ = 0; while (ISSPACE(*value)) value++; trimblanks(key, 0)[0] = 0; trimblanks(value, 0)[0] = 0; /* * Enforce the "key whitespace value" format. Disallow missing * keys or missing values. */ if (*key == 0 || *value == 0) { msg_warn("%s, line %d: expected format: key whitespace value", VSTREAM_PATH(source_fp), lineno); continue; } if (key[strlen(key) - 1] == ':') msg_warn("%s, line %d: record is in \"key: value\" format; is this an alias file?", VSTREAM_PATH(source_fp), lineno); /* * Store the value under a case-insensitive key. */ mkmap_append(mkmap, key, value); if (mkmap->dict->error) msg_fatal("table %s:%s: write error: %m", mkmap->dict->type, mkmap->dict->name); } break; } /* * Close the mapping database, and release the lock. */ mkmap_close(mkmap); /* * Cleanup. We're about to terminate, but it is a good sanity check. */ vstring_free(line_buffer); if (source_fp != VSTREAM_IN) vstream_fclose(source_fp); }
DNS_RR *smtp_domain_addr(const char *name, DNS_RR **mxrr, int misc_flags, DSN_BUF *why, int *found_myself) { DNS_RR *mx_names; DNS_RR *addr_list = 0; DNS_RR *self = 0; unsigned best_pref; unsigned best_found; int r = 0; /* Resolver flags */ const char *aname; dsb_reset(why); /* Paranoia */ /* * Preferences from DNS use 0..32767, fall-backs use 32768+. */ #define IMPOSSIBLE_PREFERENCE (~0) /* * Sanity check. */ if (smtp_dns_support == SMTP_DNS_DISABLED) msg_panic("smtp_domain_addr: DNS lookup is disabled"); if (smtp_dns_support == SMTP_DNS_DNSSEC) r |= RES_USE_DNSSEC; /* * IDNA support. */ #ifndef NO_EAI if (!allascii(name) && (aname = midna_domain_to_ascii(name)) != 0) { if (msg_verbose) msg_info("%s asciified to %s", name, aname); } else #endif aname = name; /* * Look up the mail exchanger hosts listed for this name. Sort the * results by preference. Look up the corresponding host addresses, and * truncate the list so that it contains only hosts that are more * preferred than myself. When no MX resource records exist, look up the * addresses listed for this name. * * According to RFC 974: "It is possible that the list of MXs in the * response to the query will be empty. This is a special case. If the * list is empty, mailers should treat it as if it contained one RR, an * MX RR with a preference value of 0, and a host name of REMOTE. (I.e., * REMOTE is its only MX). In addition, the mailer should do no further * processing on the list, but should attempt to deliver the message to * REMOTE." * * Normally it is OK if an MX host cannot be found in the DNS; we'll just * use a backup one, and silently ignore the better MX host. However, if * the best backup that we can find in the DNS is the local machine, then * we must remember that the local machine is not the primary MX host, or * else we will claim that mail loops back. * * XXX Optionally do A lookups even when the MX lookup didn't complete. * Unfortunately with some DNS servers this is not a transient problem. * * XXX Ideally we would perform A lookups only as far as needed. But as long * as we're looking up all the hosts, it would be better to look up the * least preferred host first, so that DNS lookup error messages make * more sense. * * XXX 2821: RFC 2821 says that the sender must shuffle equal-preference MX * hosts, whereas multiple A records per hostname must be used in the * order as received. They make the bogus assumption that a hostname with * multiple A records corresponds to one machine with multiple network * interfaces. * * XXX 2821: Postfix recognizes the local machine by looking for its own IP * address in the list of mail exchangers. RFC 2821 says one has to look * at the mail exchanger hostname as well, making the bogus assumption * that an IP address is listed only under one hostname. However, looking * at hostnames provides a partial solution for MX hosts behind a NAT * gateway. */ switch (dns_lookup(aname, T_MX, r, &mx_names, (VSTRING *) 0, why->reason)) { default: dsb_status(why, "4.4.3"); if (var_ign_mx_lookup_err) addr_list = smtp_host_addr(aname, misc_flags, why); break; case DNS_INVAL: dsb_status(why, "5.4.4"); if (var_ign_mx_lookup_err) addr_list = smtp_host_addr(aname, misc_flags, why); break; case DNS_NULLMX: dsb_status(why, "5.1.0"); break; case DNS_POLICY: dsb_status(why, "4.7.0"); break; case DNS_FAIL: dsb_status(why, "5.4.3"); if (var_ign_mx_lookup_err) addr_list = smtp_host_addr(aname, misc_flags, why); break; case DNS_OK: mx_names = dns_rr_sort(mx_names, dns_rr_compare_pref_any); best_pref = (mx_names ? mx_names->pref : IMPOSSIBLE_PREFERENCE); addr_list = smtp_addr_list(mx_names, why); if (mxrr) *mxrr = dns_rr_copy(mx_names); /* copies one record! */ dns_rr_free(mx_names); if (addr_list == 0) { /* Text does not change. */ if (var_smtp_defer_mxaddr) { /* Don't clobber the null terminator. */ if (SMTP_HAS_HARD_DSN(why)) SMTP_SET_SOFT_DSN(why); /* XXX */ /* Require some error status. */ else if (!SMTP_HAS_SOFT_DSN(why)) msg_panic("smtp_domain_addr: bad status"); } msg_warn("no MX host for %s has a valid address record", name); break; } best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE); if (msg_verbose) smtp_print_addr(name, addr_list); if ((misc_flags & SMTP_MISC_FLAG_LOOP_DETECT) && (self = smtp_find_self(addr_list)) != 0) { addr_list = smtp_truncate_self(addr_list, self->pref); if (addr_list == 0) { if (best_pref != best_found) { dsb_simple(why, "4.4.4", "unable to find primary relay for %s", name); } else { dsb_simple(why, "5.4.6", "mail for %s loops back to myself", name); } } } #define SMTP_COMPARE_ADDR(flags) \ (((flags) & SMTP_MISC_FLAG_PREF_IPV6) ? dns_rr_compare_pref_ipv6 : \ ((flags) & SMTP_MISC_FLAG_PREF_IPV4) ? dns_rr_compare_pref_ipv4 : \ dns_rr_compare_pref_any) if (addr_list && addr_list->next && var_smtp_rand_addr) { addr_list = dns_rr_shuffle(addr_list); addr_list = dns_rr_sort(addr_list, SMTP_COMPARE_ADDR(misc_flags)); } break; case DNS_NOTFOUND: addr_list = smtp_host_addr(aname, misc_flags, why); break; } /* * Clean up. */ *found_myself |= (self != 0); return (addr_list); }
static void postalias(char *map_type, char *path_name, int postalias_flags, int open_flags, int dict_flags) { VSTREAM *NOCLOBBER source_fp; VSTRING *line_buffer; MKMAP *mkmap; int lineno; int last_line; VSTRING *key_buffer; VSTRING *value_buffer; TOK822 *tok_list; TOK822 *key_list; TOK822 *colon; TOK822 *value_list; struct stat st; mode_t saved_mask; /* * Initialize. */ line_buffer = vstring_alloc(100); key_buffer = vstring_alloc(100); value_buffer = vstring_alloc(100); if ((open_flags & O_TRUNC) == 0) { /* Incremental mode. */ source_fp = VSTREAM_IN; vstream_control(source_fp, CA_VSTREAM_CTL_PATH("stdin"), CA_VSTREAM_CTL_END); } else { /* Create database. */ if (strcmp(map_type, DICT_TYPE_PROXY) == 0) msg_fatal("can't create maps via the proxy service"); dict_flags |= DICT_FLAG_BULK_UPDATE; if ((source_fp = vstream_fopen(path_name, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", path_name); } if (fstat(vstream_fileno(source_fp), &st) < 0) msg_fatal("fstat %s: %m", path_name); /* * Turn off group/other read permissions as indicated in the source file. */ if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) saved_mask = umask(022 | (~st.st_mode & 077)); /* * If running as root, run as the owner of the source file, so that the * result shows proper ownership, and so that a bug in postalias does not * allow privilege escalation. */ if ((postalias_flags & POSTALIAS_FLAG_AS_OWNER) && getuid() == 0 && (st.st_uid != geteuid() || st.st_gid != getegid())) set_eugid(st.st_uid, st.st_gid); /* * Open the database, create it when it does not exist, truncate it when * it does exist, and lock out any spectators. */ mkmap = mkmap_open(map_type, path_name, open_flags, dict_flags); /* * And restore the umask, in case it matters. */ if ((postalias_flags & POSTALIAS_FLAG_SAVE_PERM) && S_ISREG(st.st_mode)) umask(saved_mask); /* * Trap "exceptions" so that we can restart a bulk-mode update after a * recoverable error. */ for (;;) { if (dict_isjmp(mkmap->dict) != 0 && dict_setjmp(mkmap->dict) != 0 && vstream_fseek(source_fp, SEEK_SET, 0) < 0) msg_fatal("seek %s: %m", VSTREAM_PATH(source_fp)); /* * Add records to the database. */ last_line = 0; while (readllines(line_buffer, source_fp, &last_line, &lineno)) { /* * First some UTF-8 checks sans casefolding. */ if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) && !allascii(STR(line_buffer)) && !valid_utf8_string(STR(line_buffer), LEN(line_buffer))) { msg_warn("%s, line %d: non-UTF-8 input \"%s\"" " -- ignoring this line", VSTREAM_PATH(source_fp), lineno, STR(line_buffer)); continue; } /* * Tokenize the input, so that we do the right thing when a * quoted localpart contains special characters such as "@", ":" * and so on. */ if ((tok_list = tok822_scan(STR(line_buffer), (TOK822 **) 0)) == 0) continue; /* * Enforce the key:value format. Disallow missing keys, * multi-address keys, or missing values. In order to specify an * empty string or value, enclose it in double quotes. */ if ((colon = tok822_find_type(tok_list, ':')) == 0 || colon->prev == 0 || colon->next == 0 || tok822_rfind_type(colon, ',')) { msg_warn("%s, line %d: need name:value pair", VSTREAM_PATH(source_fp), lineno); tok822_free_tree(tok_list); continue; } /* * Key must be local. XXX We should use the Postfix rewriting and * resolving services to handle all address forms correctly. * However, we can't count on the mail system being up when the * alias database is being built, so we're guessing a bit. */ if (tok822_rfind_type(colon, '@') || tok822_rfind_type(colon, '%')) { msg_warn("%s, line %d: name must be local", VSTREAM_PATH(source_fp), lineno); tok822_free_tree(tok_list); continue; } /* * Split the input into key and value parts, and convert from * token representation back to string representation. Convert * the key to internal (unquoted) form, because the resolver * produces addresses in internal form. Convert the value to * external (quoted) form, because it will have to be re-parsed * upon lookup. Discard the token representation when done. */ key_list = tok_list; tok_list = 0; value_list = tok822_cut_after(colon); tok822_unlink(colon); tok822_free(colon); tok822_internalize(key_buffer, key_list, TOK822_STR_DEFL); tok822_free_tree(key_list); tok822_externalize(value_buffer, value_list, TOK822_STR_DEFL); tok822_free_tree(value_list); /* * Store the value under a case-insensitive key. */ mkmap_append(mkmap, STR(key_buffer), STR(value_buffer)); if (mkmap->dict->error) msg_fatal("table %s:%s: write error: %m", mkmap->dict->type, mkmap->dict->name); } break; } /* * Update or append sendmail and NIS signatures. */ if ((open_flags & O_TRUNC) == 0) mkmap->dict->flags |= DICT_FLAG_DUP_REPLACE; /* * Sendmail compatibility: add the @:@ signature to indicate that the * database is complete. This might be needed by NIS clients running * sendmail. */ mkmap_append(mkmap, "@", "@"); if (mkmap->dict->error) msg_fatal("table %s:%s: write error: %m", mkmap->dict->type, mkmap->dict->name); /* * NIS compatibility: add time and master info. Unlike other information, * this information MUST be written without a trailing null appended to * key or value. */ mkmap->dict->flags &= ~DICT_FLAG_TRY1NULL; mkmap->dict->flags |= DICT_FLAG_TRY0NULL; vstring_sprintf(value_buffer, "%010ld", (long) time((time_t *) 0)); #if (defined(HAS_NIS) || defined(HAS_NISPLUS)) mkmap->dict->flags &= ~DICT_FLAG_FOLD_FIX; mkmap_append(mkmap, "YP_LAST_MODIFIED", STR(value_buffer)); mkmap_append(mkmap, "YP_MASTER_NAME", var_myhostname); #endif /* * Close the alias database, and release the lock. */ mkmap_close(mkmap); /* * Cleanup. We're about to terminate, but it is a good sanity check. */ vstring_free(value_buffer); vstring_free(key_buffer); vstring_free(line_buffer); if (source_fp != VSTREAM_IN) vstream_fclose(source_fp); }