net.c revision b270c58fc227c91a88b07316d680418ec75021e9
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* Copyright (c) 1999-2013 Dovecot authors, see the included COPYING file */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi# include <sys/ucred.h> /* for FreeBSD struct xucred */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi# define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_GETPEERUCRED) && defined(MSG_WAITALL) && defined(LOCAL_CREDS)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomibool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi const unsigned char *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi unsigned int len, g, h = 0;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi h = (h << 4) + *p;
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if ((g = h & 0xf0000000UL)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi h = h ^ (g >> 24);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* copy IP to sockaddr */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic inline void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomisin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic inline void
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomisin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* IP structs may be sent across processes. Clear the whole struct
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi first to make sure it won't leak any data across processes. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic inline void sin_set_port(union sockaddr_union *so, unsigned int port)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi so->sin6.sin6_port = htons((unsigned short) port);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic inline unsigned int sin_get_port(union sockaddr_union *so)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuominet_connect_ip_full_freebsd(const struct ip_addr *ip, unsigned int port,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi for (try = 0;;) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi fd = net_connect_ip_full_freebsd(ip, port, my_ip, blocking);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi This may be just a temporary problem:
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi EADDRINUSE: busy
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi EACCES: pf may cause this if another connection used
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi the same port recently
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi/* then some kludging: */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi#define net_connect_ip_full net_connect_ip_full_freebsd
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomistatic int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (my_ip != NULL && ip->family != my_ip->family) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_warning("net_connect_ip(): ip->family != my_ip->family");
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the socket */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* set socket options */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* set our own address */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi i_error("bind(%s) failed: %m", net_ip2addr(my_ip));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* connect */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_connect_ip(const struct ip_addr *ip, unsigned int port,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return net_connect_ip_full(ip, port, my_ip, FALSE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_connect_ip_blocking(const struct ip_addr *ip, unsigned int port,
bf2d54df4f5943ec0617aadb1900bb8f40b12150Aki Tuomi return net_connect_ip_full(ip, port, my_ip, TRUE);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the socket */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* too long path */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the socket */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* connect */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_connect_unix_with_retries(const char *path, unsigned int msecs)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (fd != -1 || (errno != EAGAIN && errno != ECONNREFUSED))
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* busy. wait for a while. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi } while (timeval_diff_msecs(&now, &start) < (int)msecs);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* FreeBSD's close() fails with ECONNRESET if socket still has unsent
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi data in transmit buffer. We don't care. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_set_cork(int fd ATTR_UNUSED, bool cork ATTR_UNUSED)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return setsockopt(fd, IPPROTO_TCP, TCP_CORK, &val, sizeof(val));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_listen(const struct ip_addr *my_ip, unsigned int *port, int backlog)
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi return net_listen_full(my_ip, port, &flags, backlog);
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomiint net_listen_full(const struct ip_addr *my_ip, unsigned int *port,
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the socket */
bf2d54df4f5943ec0617aadb1900bb8f40b12150Aki Tuomi /* IPv6 is not supported by OS */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* set socket options */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* If using IPv6, bind only to IPv6 if possible. This avoids
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi ambiguities with IPv4-mapped IPv6 addresses. */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt));
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* specify the address/port we want to listen in */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* get the actual port we started listen */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (ret >= 0) {
beec8b0a6a3ece557c9acec524e82542a9641662Aki Tuomi /* start listening */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* too long path */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi /* create the socket */
4c78d9e646c4a1158d7167806937c02d86cdfc25Aki Tuomi if (setsockopt(fd, 0, LOCAL_CREDS, &on, sizeof on)) {
return fd;
int fd;
return fd;
int ret;
if (ret < 0) {
return ret;
if (ret == 0) {
errno = 0;
return ret;
return ret;
unsigned int *ips_count)
#ifdef HAVE_IPV6
int host_error;
int count;
*ips_count = 0;
#ifdef HAVE_IPV6
if (host_error != 0)
return host_error;
count++;
count = 0;
return h_errno;
count = 0;
count++;
while (count > 0) {
count--;
int ret;
if (ret != 0)
return ret;
#if defined(SO_PEERCRED)
# if defined(HAVE_STRUCT_SOCKPEERCRED)
int i, n, on;
} cdata;
#ifdef HAVE_IPV6
unsigned long ip4;
int ret;
#ifdef HAVE_IPV6
T_BEGIN {
} T_END;
if (ret == 0)
#ifdef HAVE_IPV6
int data;
return data;
#ifdef HAVE_IPV6
switch (error) {
case HOST_NOT_FOUND:
case NO_ADDRESS:
case NO_RECOVERY:
case TRY_AGAIN:
#ifdef HAVE_IPV6
return FALSE;
addr++;
return TRUE;
addr++;
return FALSE;
addr++;
return TRUE;
unsigned int *bits_r)
if (p != NULL)
if (p == NULL) {
unsigned int pos, i;
return FALSE;
return FALSE;
#ifdef HAVE_IPV6
return FALSE;
return FALSE;
return FALSE;
return FALSE;
return TRUE;