static int inet_addr_list_comp(const void *a, const void *b) { /* * In case (struct *) != (void *). */ return (sock_addr_cmp_addr(SOCK_ADDR_PTR(a), SOCK_ADDR_PTR(b))); }
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); }
int main(int argc, char **argv) { INET_ADDR_LIST list; struct sockaddr_storage *sa; MAI_HOSTADDR_STR hostaddr; INET_PROTO_INFO *proto_info; msg_vstream_init(argv[0], VSTREAM_ERR); if (argc < 3) msg_fatal("usage: %s protocols hostname...", argv[0]); proto_info = inet_proto_init(argv[0], argv[1]); argv += 1; while (--argc && *++argv) { inet_addr_list_init(&list); if (inet_addr_host(&list, *argv) == 0) msg_fatal("not found: %s", *argv); for (sa = list.addrs; sa < list.addrs + list.used; sa++) { SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); vstream_printf("%s\t%s\n", *argv, hostaddr.buf); } vstream_fflush(VSTREAM_OUT); inet_addr_list_free(&list); } return (0); }
static void inet_addr_list_print(INET_ADDR_LIST *list) { MAI_HOSTADDR_STR hostaddr; struct sockaddr_storage *sa; for (sa = list->addrs; sa < list->addrs + list->used; sa++) { SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_info("%s", hostaddr.buf); } }
int main(int unused_argc, char **argv) { INET_ADDR_LIST addr_list; INET_ADDR_LIST mask_list; MAI_HOSTADDR_STR hostaddr; MAI_HOSTADDR_STR hostmask; struct sockaddr *sa; int i; INET_PROTO_INFO *proto_info; msg_vstream_init(argv[0], VSTREAM_ERR); msg_verbose = 1; proto_info = inet_proto_init(argv[0], argv[1] ? argv[1] : INET_PROTO_NAME_ALL); inet_addr_list_init(&addr_list); inet_addr_list_init(&mask_list); inet_addr_local(&addr_list, &mask_list, proto_info->ai_family_list); if (addr_list.used == 0) msg_fatal("cannot find any active network interfaces"); if (addr_list.used == 1) msg_warn("found only one active network interface"); for (i = 0; i < addr_list.used; i++) { sa = SOCK_ADDR_PTR(addr_list.addrs + i); SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); sa = SOCK_ADDR_PTR(mask_list.addrs + i); SOCKADDR_TO_HOSTADDR(SOCK_ADDR_PTR(sa), SOCK_ADDR_LEN(sa), &hostmask, (MAI_SERVPORT_STR *) 0, 0); vstream_printf("%s/%s\n", hostaddr.buf, hostmask.buf); vstream_fflush(VSTREAM_OUT); } inet_addr_list_free(&addr_list); inet_addr_list_free(&mask_list); return (0); }
static SMTP_SESSION *smtp_connect_addr(SMTP_ITERATOR *iter, DSN_BUF *why, int sess_flags) { const char *myname = "smtp_connect_addr"; struct sockaddr_storage ss; /* remote */ struct sockaddr *sa = (struct sockaddr *) &ss; SOCKADDR_SIZE salen = sizeof(ss); MAI_HOSTADDR_STR hostaddr; DNS_RR *addr = iter->rr; unsigned port = iter->port; int sock; char *bind_addr; char *bind_var; dsb_reset(why); /* Paranoia */ /* * Sanity checks. */ if (dns_rr_to_sa(addr, port, sa, &salen) != 0) { msg_warn("%s: skip address type %s: %m", myname, dns_strtype(addr->type)); dsb_simple(why, "4.4.0", "network address conversion failed: %m"); return (0); } /* * Initialize. */ if ((sock = socket(sa->sa_family, SOCK_STREAM, 0)) < 0) msg_fatal("%s: socket: %m", myname); if (inet_windowsize > 0) set_inet_windowsize(sock, inet_windowsize); /* * Allow the sysadmin to specify the source address, for example, as "-o * smtp_bind_address=x.x.x.x" in the master.cf file. */ #ifdef HAS_IPV6 if (sa->sa_family == AF_INET6) { bind_addr = var_smtp_bind_addr6; bind_var = VAR_LMTP_SMTP(BIND_ADDR6); } else #endif if (sa->sa_family == AF_INET) { bind_addr = var_smtp_bind_addr; bind_var = VAR_LMTP_SMTP(BIND_ADDR); } else bind_var = bind_addr = ""; if (*bind_addr) { int aierr; struct addrinfo *res0; if ((aierr = hostaddr_to_sockaddr(bind_addr, (char *) 0, 0, &res0)) != 0) msg_fatal("%s: bad %s parameter: %s: %s", myname, bind_var, bind_addr, MAI_STRERROR(aierr)); if (bind(sock, res0->ai_addr, res0->ai_addrlen) < 0) msg_warn("%s: bind %s: %m", myname, bind_addr); else if (msg_verbose) msg_info("%s: bind %s", myname, bind_addr); freeaddrinfo(res0); } /* * When running as a virtual host, bind to the virtual interface so that * the mail appears to come from the "right" machine address. * * XXX The IPv6 patch expands the null host (as client endpoint) and uses * the result as the loopback address list. */ else { int count = 0; struct sockaddr *own_addr = 0; INET_ADDR_LIST *addr_list = own_inet_addr_list(); struct sockaddr_storage *s; for (s = addr_list->addrs; s < addr_list->addrs + addr_list->used; s++) { if (SOCK_ADDR_FAMILY(s) == sa->sa_family) { if (count++ > 0) break; own_addr = SOCK_ADDR_PTR(s); } } if (count == 1 && !sock_addr_in_loopback(own_addr)) { if (bind(sock, own_addr, SOCK_ADDR_LEN(own_addr)) < 0) { SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_warn("%s: bind %s: %m", myname, hostaddr.buf); } else if (msg_verbose) { SOCKADDR_TO_HOSTADDR(own_addr, SOCK_ADDR_LEN(own_addr), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); msg_info("%s: bind %s", myname, hostaddr.buf); } } } /* * Connect to the server. */ if (msg_verbose) msg_info("%s: trying: %s[%s] port %d...", myname, STR(iter->host), STR(iter->addr), ntohs(port)); return (smtp_connect_sock(sock, sa, salen, iter, why, sess_flags)); }
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); }
void master_listen_init(MASTER_SERV *serv) { const char *myname = "master_listen_init"; char *end_point; int n; MAI_HOSTADDR_STR hostaddr; struct sockaddr *sa; /* * Find out what transport we should use, then create one or more * listener sockets. Make the listener sockets non-blocking, so that * child processes don't block in accept() when multiple processes are * selecting on the same socket and only one of them gets the connection. */ switch (serv->type) { /* * UNIX-domain or stream listener endpoints always come as singlets. */ case MASTER_SERV_TYPE_UNIX: set_eugid(var_owner_uid, var_owner_gid); serv->listen_fd[0] = LOCAL_LISTEN(serv->name, serv->max_proc > var_proc_limit ? serv->max_proc : var_proc_limit, NON_BLOCKING); close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); set_ugid(getuid(), getgid()); break; /* * FIFO listener endpoints always come as singlets. */ case MASTER_SERV_TYPE_FIFO: set_eugid(var_owner_uid, var_owner_gid); serv->listen_fd[0] = fifo_listen(serv->name, 0622, NON_BLOCKING); close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); set_ugid(getuid(), getgid()); break; /* * INET-domain listener endpoints can be wildcarded (the default) or * bound to specific interface addresses. * * With dual-stack IPv4/6 systems it does not matter, we have to specify * the addresses anyway, either explicit or wild-card. */ case MASTER_SERV_TYPE_INET: for (n = 0; n < serv->listen_fd_count; n++) { sa = SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n); SOCKADDR_TO_HOSTADDR(sa, SOCK_ADDR_LEN(sa), &hostaddr, (MAI_SERVPORT_STR *) 0, 0); end_point = concatenate(hostaddr.buf, ":", MASTER_INET_PORT(serv), (char *) 0); serv->listen_fd[n] = inet_listen(end_point, serv->max_proc > var_proc_limit ? serv->max_proc : var_proc_limit, NON_BLOCKING); close_on_exec(serv->listen_fd[n], CLOSE_ON_EXEC); myfree(end_point); } break; /* * Descriptor passing endpoints always come as singlets. */ #ifdef MASTER_SERV_TYPE_PASS case MASTER_SERV_TYPE_PASS: set_eugid(var_owner_uid, var_owner_gid); serv->listen_fd[0] = PASS_LISTEN(serv->name, serv->max_proc > var_proc_limit ? serv->max_proc : var_proc_limit, NON_BLOCKING); close_on_exec(serv->listen_fd[0], CLOSE_ON_EXEC); set_ugid(getuid(), getgid()); break; #endif default: msg_panic("%s: unknown service type: %d", myname, serv->type); } }
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); } }
MASTER_SERV *get_master_ent() { VSTRING *buf = vstring_alloc(100); VSTRING *junk = vstring_alloc(100); MASTER_SERV *serv; char *cp; char *name; char *host = 0; char *port = 0; char *transport; int private; int unprivileged; /* passed on to child */ int chroot; /* passed on to child */ char *command; int n; char *bufp; char *atmp; const char *parse_err; static char *saved_interfaces = 0; char *err; if (master_fp == 0) msg_panic("get_master_ent: config file not open"); if (master_disable == 0) msg_panic("get_master_ent: no service disable list"); /* * XXX We cannot change the inet_interfaces setting for a running master * process. Listening sockets are inherited by child processes so that * closing and reopening those sockets in the master does not work. * * Another problem is that library routines still cache results that are * based on the old inet_interfaces setting. It is too much trouble to * recompute everything. * * In order to keep our data structures consistent we ignore changes in * inet_interfaces settings, and issue a warning instead. */ if (saved_interfaces == 0) saved_interfaces = mystrdup(var_inet_interfaces); /* * Skip blank lines and comment lines. */ for (;;) { if (readllines(buf, master_fp, &master_line_last, &master_line) == 0) { vstring_free(buf); vstring_free(junk); return (0); } bufp = vstring_str(buf); if ((cp = mystrtok(&bufp, master_blanks)) == 0) continue; name = cp; transport = get_str_ent(&bufp, "transport type", (char *) 0); vstring_sprintf(junk, "%s/%s", name, transport); if (match_service_match(master_disable, vstring_str(junk)) == 0) break; } /* * Parse one logical line from the configuration file. Initialize service * structure members in order. */ serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV)); serv->next = 0; /* * Flags member. */ serv->flags = 0; /* * All servers busy warning timer. */ serv->busy_warn_time = 0; /* * Service name. Syntax is transport-specific. */ serv->ext_name = mystrdup(name); /* * Transport type: inet (wild-card listen or virtual) or unix. */ #define STR_SAME !strcmp if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) { if (!STR_SAME(saved_interfaces, var_inet_interfaces)) { msg_warn("service %s: ignoring %s change", serv->ext_name, VAR_INET_INTERFACES); msg_warn("to change %s, stop and start Postfix", VAR_INET_INTERFACES); } serv->type = MASTER_SERV_TYPE_INET; atmp = mystrdup(name); if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0) fatal_with_context("%s in \"%s\"", parse_err, name); if (*host) { serv->flags |= MASTER_FLAG_INETHOST;/* host:port */ MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *) mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv))); inet_addr_list_init(MASTER_INET_ADDRLIST(serv)); if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0) fatal_with_context("bad hostname or network address: %s", name); inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; } else { MASTER_INET_ADDRLIST(serv) = strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ? own_inet_addr_list() : /* virtual */ wildcard_inet_addr_list(); /* wild-card */ inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; } MASTER_INET_PORT(serv) = mystrdup(port); for (n = 0; /* see below */ ; n++) { if (n >= MASTER_INET_ADDRLIST(serv)->used) { serv->flags |= MASTER_FLAG_LOCAL_ONLY; break; } if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n))) break; } } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) { serv->type = MASTER_SERV_TYPE_UNIX; serv->listen_fd_count = 1; serv->flags |= MASTER_FLAG_LOCAL_ONLY; } else if (STR_SAME(transport, MASTER_XPORT_NAME_UXDG)) { serv->type = MASTER_SERV_TYPE_UXDG; serv->listen_fd_count = 1; serv->flags |= MASTER_FLAG_LOCAL_ONLY; } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) { serv->type = MASTER_SERV_TYPE_FIFO; serv->listen_fd_count = 1; serv->flags |= MASTER_FLAG_LOCAL_ONLY; #ifdef MASTER_SERV_TYPE_PASS } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) { serv->type = MASTER_SERV_TYPE_PASS; serv->listen_fd_count = 1; /* If this is a connection screener, remote clients are likely. */ #endif } else { fatal_with_context("bad transport type: %s", transport); } /* * Service class: public or private. */ private = get_bool_ent(&bufp, "private", "y");