Пример #1
0
static int ial_procnet_ifinet6(INET_ADDR_LIST *addr_list,
			               INET_ADDR_LIST *mask_list)
{
    const char *myname = "inet_addr_local[procnet_ifinet6]";
    FILE   *fp;
    char    buf[BUFSIZ];
    unsigned plen;
    VSTRING *addrbuf;
    struct sockaddr_in6 addr;
    struct sockaddr_in6 mask;

    /*
     * Example: 00000000000000000000000000000001 01 80 10 80 lo
     * 
     * Fields: address, interface index, prefix length, scope value
     * (net/ipv6.h), interface flags (linux/rtnetlink.h), device name.
     * 
     * FIX 200501 The IPv6 patch used fscanf(), which will hang on unexpected
     * input. Use fgets() + sscanf() instead.
     */
    if ((fp = fopen(_PATH_PROCNET_IFINET6, "r")) != 0) {
	addrbuf = vstring_alloc(MAI_V6ADDR_BYTES + 1);
	memset((void *) &addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
#ifdef HAS_SA_LEN
	addr.sin6_len = sizeof(addr);
#endif
	mask = addr;
	while (fgets(buf, sizeof(buf), fp) != 0) {
	    /* 200501 hex_decode() is light-weight compared to getaddrinfo(). */
	    if (hex_decode(addrbuf, buf, MAI_V6ADDR_BYTES * 2) == 0
		|| sscanf(buf + MAI_V6ADDR_BYTES * 2, " %*x %x", &plen) != 1
		|| plen > MAI_V6ADDR_BITS) {
		msg_warn("unexpected data in %s - skipping IPv6 configuration",
			 _PATH_PROCNET_IFINET6);
		break;
	    }
	    /* vstring_str(addrbuf) has worst-case alignment. */
	    addr.sin6_addr = *(struct in6_addr *) vstring_str(addrbuf);
	    inet_addr_list_append(addr_list, SOCK_ADDR_PTR(&addr));

	    memset((void *) &mask.sin6_addr, ~0, sizeof(mask.sin6_addr));
	    mask_addr((unsigned char *) &mask.sin6_addr,
		      sizeof(mask.sin6_addr), plen);
	    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask));
	}
	vstring_free(addrbuf);
	fclose(fp);				/* FIX 200501 */
    } else {
	msg_warn("can't open %s (%m) - skipping IPv6 configuration",
		 _PATH_PROCNET_IFINET6);
    }
    return (0);
}
Пример #2
0
int     inet_addr_host(INET_ADDR_LIST *addr_list, const char *hostname)
{
    const char *myname = "inet_addr_host";
    int     sock;
    struct addrinfo *res0;
    struct addrinfo *res;
    int     aierr;
    ssize_t hostnamelen;
    const char *hname;
    const char *serv;
    int     initial_count = addr_list->used;
    INET_PROTO_INFO *proto_info;

    /*
     * The use of square brackets around an IPv6 addresses is required, even
     * though we don't enforce it as it'd make the code unnecessarily
     * complicated.
     * 
     * XXX AIX 5.1 getaddrinfo() does not allow "0" as service, regardless of
     * whether or not a host is specified.
     */
    if (*hostname == 0) {
	hname = 0;
	serv = "1";
    } else if (*hostname == '['
	       && hostname[(hostnamelen = strlen(hostname)) - 1] == ']') {
	hname = mystrndup(hostname + 1, hostnamelen - 2);
	serv = 0;
    } else {
	hname = hostname;
	serv = 0;
    }

    proto_info = inet_proto_info();
    if ((aierr = hostname_to_sockaddr(hname, serv, SOCK_STREAM, &res0)) == 0) {
	for (res = res0; res; res = res->ai_next) {

	    /*
	     * Safety net.
	     */
	    if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
		msg_info("%s: skipping address family %d for host \"%s\"",
			 myname, res->ai_family, hostname);
		continue;
	    }

	    /*
	     * On Linux systems it is not unusual for user-land to be out of
	     * sync with kernel-land. When this is the case we try to be
	     * helpful and filter out address families that the library
	     * claims to understand but that are not supported by the kernel.
	     */
	    if ((sock = socket(res->ai_family, SOCK_STREAM, 0)) < 0) {
		msg_warn("%s: skipping address family %d: %m",
			 myname, res->ai_family);
		continue;
	    }
	    if (close(sock))
		msg_warn("%s: close socket: %m", myname);

	    inet_addr_list_append(addr_list, res->ai_addr);
	}
	freeaddrinfo(res0);
    }
    if (hname && hname != hostname)
	myfree((void *) hname);

    return (addr_list->used - initial_count);
}
Пример #3
0
static int ial_siocgif(INET_ADDR_LIST *addr_list,
		               INET_ADDR_LIST *mask_list,
		               int af)
{
    const char *myname = "inet_addr_local[siocgif]";
    struct in_addr addr;
    struct ifconf ifc;
    struct ifreq *ifr;
    struct ifreq *ifr_mask;
    struct ifreq *the_end;
    int     sock;
    VSTRING *buf;

    /*
     * Get the network interface list. XXX The socket API appears to have no
     * function that returns the number of network interfaces, so we have to
     * guess how much space is needed to store the result.
     * 
     * On BSD-derived systems, ioctl SIOCGIFCONF returns as much information as
     * possible, leaving it up to the application to repeat the request with
     * a larger buffer if the result caused a tight fit.
     * 
     * Other systems, such as Solaris 2.5, generate an EINVAL error when the
     * buffer is too small for the entire result. Workaround: ignore EINVAL
     * errors and repeat the request with a larger buffer. The downside is
     * that the program can run out of memory due to a non-memory problem,
     * making it more difficult than necessary to diagnose the real problem.
     */
    sock = ial_socket(af);
    if (sock < 0)
	return (0);
    buf = vstring_alloc(1024);
    for (;;) {
	ifc.ifc_len = vstring_avail(buf);
	ifc.ifc_buf = vstring_str(buf);
	if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
	    if (errno != EINVAL)
		msg_fatal("%s: ioctl SIOCGIFCONF: %m", myname);
	} else if (ifc.ifc_len < vstring_avail(buf) / 2)
	    break;
	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
    }

    the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
    for (ifr = ifc.ifc_req; ifr < the_end;) {
	if (ifr->ifr_addr.sa_family != af) {
	    ifr = NEXT_INTERFACE(ifr);
	    continue;
	}
	if (af == AF_INET) {
	    addr = ((struct sockaddr_in *) & ifr->ifr_addr)->sin_addr;
	    if (addr.s_addr != INADDR_ANY) {
		inet_addr_list_append(addr_list, &ifr->ifr_addr);
		if (mask_list) {
		    ifr_mask = (struct ifreq *) mymalloc(IFREQ_SIZE(ifr));
		    memcpy((void *) ifr_mask, (void *) ifr, IFREQ_SIZE(ifr));
		    if (ioctl(sock, SIOCGIFNETMASK, ifr_mask) < 0)
			msg_fatal("%s: ioctl SIOCGIFNETMASK: %m", myname);

		    /*
		     * Note that this SIOCGIFNETMASK has truly screwed up the
		     * contents of sa_len/sa_family. We must fix this
		     * manually to have correct addresses.   --dcs
		     */
		    ifr_mask->ifr_addr.sa_family = af;
#ifdef HAS_SA_LEN
		    ifr_mask->ifr_addr.sa_len = sizeof(struct sockaddr_in);
#endif
		    inet_addr_list_append(mask_list, &ifr_mask->ifr_addr);
		    myfree((void *) ifr_mask);
		}
	    }
	}
#ifdef HAS_IPV6
	else if (af == AF_INET6) {
	    struct sockaddr *sa;

	    sa = SOCK_ADDR_PTR(&ifr->ifr_addr);
	    if (!(IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))) {
		inet_addr_list_append(addr_list, sa);
		if (mask_list) {
		    /* XXX Assume /128 for everything */
		    struct sockaddr_in6 mask6;

		    mask6 = *SOCK_ADDR_IN6_PTR(sa);
		    memset((void *) &mask6.sin6_addr, ~0,
			   sizeof(mask6.sin6_addr));
		    inet_addr_list_append(mask_list, SOCK_ADDR_PTR(&mask6));
		}
	    }
	}
#endif
	ifr = NEXT_INTERFACE(ifr);
    }
    vstring_free(buf);
    (void) close(sock);
    return (0);
}
Пример #4
0
static int ial_siocglif(INET_ADDR_LIST *addr_list,
			        INET_ADDR_LIST *mask_list,
			        int af)
{
    const char *myname = "inet_addr_local[siocglif]";
    struct lifconf lifc;
    struct lifreq *lifr;
    struct lifreq *lifr_mask;
    struct lifreq *the_end;
    struct sockaddr *sa;
    int     sock;
    VSTRING *buf;

    /*
     * See also comments in ial_siocgif()
     */
    if (af != AF_INET && af != AF_INET6)
	msg_fatal("%s: address family was %d, must be AF_INET (%d) or "
		  "AF_INET6 (%d)", myname, af, AF_INET, AF_INET6);
    sock = ial_socket(af);
    if (sock < 0)
	return (0);
    buf = vstring_alloc(1024);
    for (;;) {
	memset(&lifc, 0, sizeof(lifc));
	lifc.lifc_family = AF_UNSPEC;		/* XXX Why??? */
	lifc.lifc_len = vstring_avail(buf);
	lifc.lifc_buf = vstring_str(buf);
	if (ioctl(sock, SIOCGLIFCONF, (char *) &lifc) < 0) {
	    if (errno != EINVAL)
		msg_fatal("%s: ioctl SIOCGLIFCONF: %m", myname);
	} else if (lifc.lifc_len < vstring_avail(buf) / 2)
	    break;
	VSTRING_SPACE(buf, vstring_avail(buf) * 2);
    }

    the_end = (struct lifreq *) (lifc.lifc_buf + lifc.lifc_len);
    for (lifr = lifc.lifc_req; lifr < the_end;) {
	sa = (struct sockaddr *) &lifr->lifr_addr;
	if (sa->sa_family != af) {
	    lifr = NEXT_INTERFACE(lifr);
	    continue;
	}
	if (af == AF_INET) {
	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY) {
		lifr = NEXT_INTERFACE(lifr);
		continue;
	    }
#ifdef HAS_IPV6
	} else if (af == AF_INET6) {
	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa))) {
		lifr = NEXT_INTERFACE(lifr);
		continue;
	    }
	}
#endif
	inet_addr_list_append(addr_list, sa);
	if (mask_list) {
	    lifr_mask = (struct lifreq *) mymalloc(sizeof(struct lifreq));
	    memcpy((void *) lifr_mask, (void *) lifr, sizeof(struct lifreq));
	    if (ioctl(sock, SIOCGLIFNETMASK, lifr_mask) < 0)
		msg_fatal("%s: ioctl(SIOCGLIFNETMASK): %m", myname);
	    /* XXX: Check whether sa_len/family are honoured --dcs */
	    inet_addr_list_append(mask_list,
				  (struct sockaddr *) &lifr_mask->lifr_addr);
	    myfree((void *) lifr_mask);
	}
	lifr = NEXT_INTERFACE(lifr);
    }
    vstring_free(buf);
    (void) close(sock);
    return (0);
}
Пример #5
0
static int ial_getifaddrs(INET_ADDR_LIST *addr_list,
			          INET_ADDR_LIST *mask_list,
			          int af)
{
    const char *myname = "inet_addr_local[getifaddrs]";
    struct ifaddrs *ifap, *ifa;
    struct sockaddr *sa, *sam;

    if (getifaddrs(&ifap) < 0)
	msg_fatal("%s: getifaddrs: %m", myname);

    /*
     * Get the address of each IP network interface. According to BIND we
     * must include interfaces that are down because the machine may still
     * receive packets for that address (yes, via some other interface).
     * Having no way to verify this claim on every machine, I will give them
     * the benefit of the doubt.
     * 
     * FIX 200501: The IPv6 patch did not report NetBSD loopback interfaces;
     * fixed by replacing IFF_RUNNING by IFF_UP.
     * 
     * FIX 200501: The IPV6 patch did not skip wild-card interface addresses
     * (tested on FreeBSD).
     */
    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
	if (!(ifa->ifa_flags & IFF_UP) || ifa->ifa_addr == 0)
	    continue;
	sa = ifa->ifa_addr;
	if (af != AF_UNSPEC && sa->sa_family != af)
	    continue;
	sam = ifa->ifa_netmask;
	if (sam == 0) {
	    /* XXX In mynetworks, a null netmask would match everyone. */
	    msg_warn("ignoring interface with null netmask, address family %d",
		     sa->sa_family);
	    continue;
	}
	switch (sa->sa_family) {
	case AF_INET:
	    if (SOCK_ADDR_IN_ADDR(sa).s_addr == INADDR_ANY)
		continue;
	    break;
#ifdef HAS_IPV6
	case AF_INET6:
	    if (IN6_IS_ADDR_UNSPECIFIED(&SOCK_ADDR_IN6_ADDR(sa)))
		continue;
	    break;
#endif
	default:
	    continue;
	}

	inet_addr_list_append(addr_list, sa);
	if (mask_list != 0) {

	    /*
	     * Unfortunately, sa_len/sa_family may be broken in the netmask
	     * sockaddr structure. We must fix this manually to have correct
	     * addresses.   --dcs
	     */
#ifdef HAS_SA_LEN
	    sam->sa_len = sa->sa_family == AF_INET6 ?
		sizeof(struct sockaddr_in6) :
		sizeof(struct sockaddr_in);
#endif
	    sam->sa_family = sa->sa_family;
	    inet_addr_list_append(mask_list, sam);
	}
    }
    freeifaddrs(ifap);
    return (0);
}
Пример #6
0
static void own_inet_addr_init(INET_ADDR_LIST *addr_list,
			               INET_ADDR_LIST *mask_list)
{
    INET_ADDR_LIST local_addrs;
    INET_ADDR_LIST local_masks;
    char   *hosts;
    char   *host;
    const char *sep = " \t,";
    char   *bufp;
    int     nvirtual;
    int     nlocal;
    MAI_HOSTADDR_STR hostaddr;
    struct sockaddr_storage *sa;
    struct sockaddr_storage *ma;

    inet_addr_list_init(addr_list);
    inet_addr_list_init(mask_list);

    /*
     * If we are listening on all interfaces (default), ask the system what
     * the interfaces are.
     */
    if (strcmp(var_inet_interfaces, INET_INTERFACES_ALL) == 0) {
	if (inet_addr_local(addr_list, mask_list,
			    inet_proto_info()->ai_family_list) == 0)
	    msg_fatal("could not find any active network interfaces");
    }

    /*
     * Select all loopback interfaces from the system's available interface
     * list.
     */
    else if (strcmp(var_inet_interfaces, INET_INTERFACES_LOCAL) == 0) {
	inet_addr_list_init(&local_addrs);
	inet_addr_list_init(&local_masks);
	if (inet_addr_local(&local_addrs, &local_masks,
			    inet_proto_info()->ai_family_list) == 0)
	    msg_fatal("could not find any active network interfaces");
	for (sa = local_addrs.addrs, ma = local_masks.addrs;
	     sa < local_addrs.addrs + local_addrs.used; sa++, ma++) {
	    if (sock_addr_in_loopback(SOCK_ADDR_PTR(sa))) {
		inet_addr_list_append(addr_list, SOCK_ADDR_PTR(sa));
		inet_addr_list_append(mask_list, SOCK_ADDR_PTR(ma));
	    }
	}
	inet_addr_list_free(&local_addrs);
	inet_addr_list_free(&local_masks);
    }

    /*
     * If we are supposed to be listening only on specific interface
     * addresses (virtual hosting), look up the addresses of those
     * interfaces.
     */
    else {
	bufp = hosts = mystrdup(var_inet_interfaces);
	while ((host = mystrtok(&bufp, sep)) != 0)
	    if (inet_addr_host(addr_list, host) == 0)
		msg_fatal("config variable %s: host not found: %s",
			  VAR_INET_INTERFACES, host);
	myfree(hosts);

	/*
	 * Weed out duplicate IP addresses. Duplicates happen when the same
	 * IP address is listed under multiple hostnames. If we don't weed
	 * out duplicates, Postfix can suddenly stop working after the DNS is
	 * changed.
	 */
	inet_addr_list_uniq(addr_list);

	/*
	 * Find out the netmask for each virtual interface, by looking it up
	 * among all the local interfaces.
	 */
	inet_addr_list_init(&local_addrs);
	inet_addr_list_init(&local_masks);
	if (inet_addr_local(&local_addrs, &local_masks,
			    inet_proto_info()->ai_family_list) == 0)
	    msg_fatal("could not find any active network interfaces");
	for (nvirtual = 0; nvirtual < addr_list->used; nvirtual++) {
	    for (nlocal = 0; /* see below */ ; nlocal++) {
		if (nlocal >= local_addrs.used) {
		    SOCKADDR_TO_HOSTADDR(
				 SOCK_ADDR_PTR(addr_list->addrs + nvirtual),
				 SOCK_ADDR_LEN(addr_list->addrs + nvirtual),
				      &hostaddr, (MAI_SERVPORT_STR *) 0, 0);
		    msg_fatal("parameter %s: no local interface found for %s",
			      VAR_INET_INTERFACES, hostaddr.buf);
		}
		if (SOCK_ADDR_EQ_ADDR(addr_list->addrs + nvirtual,
				      local_addrs.addrs + nlocal)) {
		    inet_addr_list_append(mask_list,
				 SOCK_ADDR_PTR(local_masks.addrs + nlocal));
		    break;
		}
	    }
	}
	inet_addr_list_free(&local_addrs);
	inet_addr_list_free(&local_masks);
    }
}