示例#1
0
static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host,
                             unsigned pref, DSN_BUF *why)
{
    const char *myname = "smtp_addr_one";
    DNS_RR *addr = 0;
    DNS_RR *rr;
    int     aierr;
    struct addrinfo *res0;
    struct addrinfo *res;
    INET_PROTO_INFO *proto_info = inet_proto_info();
    int     found;

    if (msg_verbose)
        msg_info("%s: host %s", myname, host);

    /*
     * Interpret a numerical name as an address.
     */
    if (hostaddr_to_sockaddr(host, (char *) 0, 0, &res0) == 0
            && strchr((char *) proto_info->sa_family_list, res0->ai_family) != 0) {
        if ((addr = dns_sa_to_rr(host, pref, res0->ai_addr)) == 0)
            msg_fatal("host %s: conversion error for address family %d: %m",
                      host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
        addr_list = dns_rr_append(addr_list, addr);
        freeaddrinfo(res0);
        return (addr_list);
    }

    /*
     * Use DNS lookup, but keep the option open to use native name service.
     *
     * XXX A soft error dominates past and future hard errors. Therefore we
     * should not clobber a soft error text and status code.
     */
    if (smtp_host_lookup_mask & SMTP_HOST_FLAG_DNS) {
        switch (dns_lookup_v(host, smtp_dns_res_opt, &addr, (VSTRING *) 0,
                             why->reason, DNS_REQ_FLAG_NONE,
                             proto_info->dns_atype_list)) {
        case DNS_OK:
            for (rr = addr; rr; rr = rr->next)
                rr->pref = pref;
            addr_list = dns_rr_append(addr_list, addr);
            return (addr_list);
        default:
            dsb_status(why, "4.4.3");
            return (addr_list);
        case DNS_FAIL:
            dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.3" : "5.4.3");
            return (addr_list);
        case DNS_INVAL:
            dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
            return (addr_list);
        case DNS_NOTFOUND:
            dsb_status(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4");
            /* maybe native naming service will succeed */
            break;
        }
    }

    /*
     * Use the native name service which also looks in /etc/hosts.
     *
     * XXX A soft error dominates past and future hard errors. Therefore we
     * should not clobber a soft error text and status code.
     */
#define RETRY_AI_ERROR(e) \
        ((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)
#ifdef EAI_NODATA
#define DSN_NOHOST(e) \
	((e) == EAI_AGAIN || (e) == EAI_NODATA || (e) == EAI_NONAME)
#else
#define DSN_NOHOST(e) \
	((e) == EAI_AGAIN || (e) == EAI_NONAME)
#endif

    if (smtp_host_lookup_mask & SMTP_HOST_FLAG_NATIVE) {
        if ((aierr = hostname_to_sockaddr(host, (char *) 0, 0, &res0)) != 0) {
            dsb_simple(why, (SMTP_HAS_SOFT_DSN(why) || RETRY_AI_ERROR(aierr)) ?
                       (DSN_NOHOST(aierr) ? "4.4.4" : "4.3.0") :
                       (DSN_NOHOST(aierr) ? "5.4.4" : "5.3.0"),
                       "unable to look up host %s: %s",
                       host, MAI_STRERROR(aierr));
        } else {
            for (found = 0, res = res0; res != 0; res = res->ai_next) {
                if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
                    msg_info("skipping address family %d for host %s",
                             res->ai_family, host);
                    continue;
                }
                found++;
                if ((addr = dns_sa_to_rr(host, pref, res->ai_addr)) == 0)
                    msg_fatal("host %s: conversion error for address family %d: %m",
                              host, ((struct sockaddr *) (res0->ai_addr))->sa_family);
                addr_list = dns_rr_append(addr_list, addr);
            }
            freeaddrinfo(res0);
            if (found == 0) {
                dsb_simple(why, SMTP_HAS_SOFT_DSN(why) ? "4.4.4" : "5.4.4",
                           "%s: host not found", host);
            }
            return (addr_list);
        }
    }

    /*
     * No further alternatives for host lookup.
     */
    return (addr_list);
}
示例#2
0
DNS_RR *smtp_domain_addr(char *name, 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;

    dsb_reset(why);				/* Paranoia */

    /*
     * Preferences from DNS use 0..32767, fall-backs use 32768+.
     */
#define IMPOSSIBLE_PREFERENCE	(~0)

    /*
     * Sanity check.
     */
    if (var_disable_dns)
        msg_panic("smtp_domain_addr: DNS lookup is disabled");

    /*
     * 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(name, T_MX, 0, &mx_names, (VSTRING *) 0, why->reason)) {
    default:
        dsb_status(why, "4.4.3");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    case DNS_INVAL:
        dsb_status(why, "5.4.4");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, misc_flags, why);
        break;
    case DNS_FAIL:
        dsb_status(why, "5.4.3");
        if (var_ign_mx_lookup_err)
            addr_list = smtp_host_addr(name, 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);
        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(name, misc_flags, why);
        break;
    }

    /*
     * Clean up.
     */
    *found_myself |= (self != 0);
    return (addr_list);
}
示例#3
0
MBOX   *mbox_open(const char *path, int flags, mode_t mode, struct stat * st,
		          uid_t chown_uid, gid_t chown_gid,
		          int lock_style, const char *def_dsn,
		          DSN_BUF *why)
{
    struct stat local_statbuf;
    MBOX   *mp;
    int     locked = 0;
    VSTREAM *fp;

    if (st == 0)
	st = &local_statbuf;

    /*
     * If this is a regular file, create a dotlock file. This locking method
     * does not work well over NFS, but it is better than some alternatives.
     * With NFS, creating files atomically is a problem, and a successful
     * operation can fail with EEXIST.
     * 
     * If filename.lock can't be created for reasons other than "file exists",
     * issue only a warning if the application says it is non-fatal. This is
     * for bass-awkward compatibility with existing installations that
     * deliver to files in non-writable directories.
     * 
     * We dot-lock the file before opening, so we must avoid doing silly things
     * like dot-locking /dev/null. Fortunately, deliveries to non-mailbox
     * files execute with recipient privileges, so we don't have to worry
     * about creating dotlock files in places where the recipient would not
     * be able to write.
     * 
     * Note: we use stat() to follow symlinks, because safe_open() allows the
     * target to be a root-owned symlink, and we don't want to create dotlock
     * files for /dev/null or other non-file objects.
     */
    if ((lock_style & MBOX_DOT_LOCK)
	&& (stat(path, st) < 0 || S_ISREG(st->st_mode))) {
	if (dot_lockfile(path, why->reason) == 0) {
	    locked |= MBOX_DOT_LOCK;
	} else if (errno == EEXIST) {
	    dsb_status(why, mbox_dsn(EAGAIN, def_dsn));
	    return (0);
	} else if (lock_style & MBOX_DOT_LOCK_MAY_FAIL) {
	    msg_warn("%s", vstring_str(why->reason));
	} else {
	    dsb_status(why, mbox_dsn(errno, def_dsn));
	    return (0);
	}
    }

    /*
     * Open or create the target file. In case of a privileged open, the
     * privileged user may be attacked with hard/soft link tricks in an
     * unsafe parent directory. In case of an unprivileged open, the mail
     * system may be attacked by a malicious user-specified path, or the
     * unprivileged user may be attacked with hard/soft link tricks in an
     * unsafe parent directory. Open non-blocking to fend off attacks
     * involving non-file targets.
     */
    if ((fp = safe_open(path, flags | O_NONBLOCK, mode, st,
			chown_uid, chown_gid, why->reason)) == 0) {
	dsb_status(why, mbox_dsn(errno, def_dsn));
	if (locked & MBOX_DOT_LOCK)
	    dot_unlockfile(path);
	return (0);
    }
    close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC);

    /*
     * If this is a regular file, acquire kernel locks. flock() locks are not
     * intended to work across a network; fcntl() locks are supposed to work
     * over NFS, but in the real world, NFS lock daemons often have serious
     * problems.
     */
#define HUNKY_DORY(lock_mask, myflock_style) ((lock_style & (lock_mask)) == 0 \
         || deliver_flock(vstream_fileno(fp), (myflock_style), why->reason) == 0)

    if (S_ISREG(st->st_mode)) {
	if (HUNKY_DORY(MBOX_FLOCK_LOCK, MYFLOCK_STYLE_FLOCK)
	    && HUNKY_DORY(MBOX_FCNTL_LOCK, MYFLOCK_STYLE_FCNTL)) {
	    locked |= lock_style;
	} else {
	    dsb_status(why, mbox_dsn(errno, def_dsn));
	    if (locked & MBOX_DOT_LOCK)
		dot_unlockfile(path);
	    vstream_fclose(fp);
	    return (0);
	}
    }

    /*
     * Sanity check: reportedly, GNU POP3D creates a new mailbox file and
     * deletes the old one. This does not play well with software that opens
     * the mailbox first and then locks it, such as software that that uses
     * FCNTL or FLOCK locks on open file descriptors (some UNIX systems don't
     * use dotlock files).
     * 
     * To detect that GNU POP3D deletes the mailbox file we look at the target
     * file hard-link count. Note that safe_open() guarantees a hard-link
     * count of 1, so any change in this count is a sign of trouble.
     */
    if (S_ISREG(st->st_mode)
	&& (fstat(vstream_fileno(fp), st) < 0 || st->st_nlink != 1)) {
	vstring_sprintf(why->reason, "target file status changed unexpectedly");
	dsb_status(why, mbox_dsn(EAGAIN, def_dsn));
	msg_warn("%s: file status changed unexpectedly", path);
	if (locked & MBOX_DOT_LOCK)
	    dot_unlockfile(path);
	vstream_fclose(fp);
	return (0);
    }
    mp = (MBOX *) mymalloc(sizeof(*mp));
    mp->path = mystrdup(path);
    mp->fp = fp;
    mp->locked = locked;
    return (mp);
}