<commit_before>/* Copyright (c) 1999-2012 Dovecot authors, see the included COPYING file */

#define _GNU_SOURCE /* For Linux's struct ucred */
#include "lib.h"
#include "close-keep-errno.h"
#include "fd-set-nonblock.h"
#include "time-util.h"
#include "network.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#ifdef HAVE_UCRED_H
#  include <ucred.h> /* for getpeerucred() */
#endif

union sockaddr_union {
	struct sockaddr sa;
	struct sockaddr_in sin;
#ifdef HAVE_IPV6
	struct sockaddr_in6 sin6;
#endif
};

union sockaddr_union_unix {
	struct sockaddr sa;
	struct sockaddr_un un;
};

#ifdef HAVE_IPV6
#  define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
	sizeof(so.sin6) : sizeof(so.sin))
#else
#  define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
#endif

bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
{
	return net_ip_cmp(ip1, ip2) == 0;
}

int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2)
{
	if (ip1->family != ip2->family)
		return ip1->family - ip2->family;

#ifdef HAVE_IPV6
	if (ip1->family == AF_INET6)
		return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6));
#endif

	return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4));
}

unsigned int net_ip_hash(const struct ip_addr *ip)
{
        const unsigned char *p;
	unsigned int len, g, h = 0;

#ifdef HAVE_IPV6
	if (ip->family == AF_INET6) {
		p = ip->u.ip6.s6_addr;
		len = sizeof(ip->u.ip6);
	} else
#endif
	{
		return ip->u.ip4.s_addr;
	}

	for (; len > 0; len--, p++) {
		h = (h << 4) + *p;
		if ((g = h & 0xf0000000UL)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}
	}

	return h;
}

/* copy IP to sockaddr */
static inline void
sin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
{
	if (ip == NULL) {
#ifdef HAVE_IPV6
		so->sin6.sin6_family = AF_INET6;
		so->sin6.sin6_addr = in6addr_any;
#else
		so->sin.sin_family = AF_INET;
		so->sin.sin_addr.s_addr = INADDR_ANY;
#endif
		return;
	}

	so->sin.sin_family = ip->family;
#ifdef HAVE_IPV6
	if (ip->family == AF_INET6)
		memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6));
	else
#endif
		memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4));
}

static inline void
sin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
{
	/* IP structs may be sent across processes. Clear the whole struct
	   first to make sure it won't leak any data across processes. */
	memset(ip, 0, sizeof(*ip));

	ip->family = so->sin.sin_family;

#ifdef HAVE_IPV6
	if (ip->family == AF_INET6)
		memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6));
	else
#endif
	if (ip->family == AF_INET)
		memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4));
	else
		memset(&ip->u, 0, sizeof(ip->u));
}

static inline void sin_set_port(union sockaddr_union *so, unsigned int port)
{
#ifdef HAVE_IPV6
	if (so->sin.sin_family == AF_INET6)
                so->sin6.sin6_port = htons((unsigned short) port);
	else
#endif
		so->sin.sin_port = htons((unsigned short) port);
}

static inline unsigned int sin_get_port(union sockaddr_union *so)
{
#ifdef HAVE_IPV6
	if (so->sin.sin_family == AF_INET6)
		return ntohs(so->sin6.sin6_port);
#endif
	if (so->sin.sin_family == AF_INET)
		return ntohs(so->sin.sin_port);

	return 0;
}

#ifdef __FreeBSD__
static int
net_connect_ip_full_freebsd(const struct ip_addr *ip, unsigned int port,
			    const struct ip_addr *my_ip, bool blocking);

static int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
			       const struct ip_addr *my_ip, bool blocking)
{
	int fd, try;

	for (try = 0;;) {
		fd = net_connect_ip_full_freebsd(ip, port, my_ip, blocking);
		if (fd != -1 || ++try == 5 ||
		    (errno != EADDRINUSE && errno != EACCES))
			break;
		/*
		   This may be just a temporary problem:

		   EADDRINUSE: busy
		   EACCES: pf may cause this if another connection used
		           the same port recently
		*/
	}
	return fd;
}
/* then some kludging: */
#define net_connect_ip_full net_connect_ip_full_freebsd
#endif

static int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
			       const struct ip_addr *my_ip, bool blocking)
{
	union sockaddr_union so;
	int fd, ret, opt = 1;

	if (my_ip != NULL && ip->family != my_ip->family) {
		i_warning("net_connect_ip(): ip->family != my_ip->family");
                my_ip = NULL;
	}

	/* create the socket */
	memset(&so, 0, sizeof(so));
        so.sin.sin_family = ip->family;
	fd = socket(ip->family, SOCK_STREAM, 0);

	if (fd == -1) {
		i_error("socket() failed: %m");
		return -1;
	}

	/* set socket options */
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
	if (!blocking)
		net_set_nonblock(fd, TRUE);

	/* set our own address */
	if (my_ip != NULL) {
		sin_set_ip(&so, my_ip);
		if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
			i_error("bind(%s) failed: %m", net_ip2addr(my_ip));
			close_keep_errno(fd);
			return -1;
		}
	}

	/* connect */
	sin_set_ip(&so, ip);
	sin_set_port(&so, port);
	ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));

#ifndef WIN32
	if (ret < 0 && errno != EINPROGRESS)
#else
	if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
#endif
	{
                close_keep_errno(fd);
		return -1;
	}

	return fd;
}
#ifdef __FreeBSD__
#  undef net_connect_ip_full
#endif

int net_connect_ip(const struct ip_addr *ip, unsigned int port,
		   const struct ip_addr *my_ip)
{
	return net_connect_ip_full(ip, port, my_ip, FALSE);
}

int net_connect_ip_blocking(const struct ip_addr *ip, unsigned int port,
			    const struct ip_addr *my_ip)
{
	return net_connect_ip_full(ip, port, my_ip, TRUE);
}

int net_try_bind(const struct ip_addr *ip)
{
	union sockaddr_union so;
	int fd;

	/* create the socket */
	memset(&so, 0, sizeof(so));
        so.sin.sin_family = ip->family;
	fd = socket(ip->family, SOCK_STREAM, 0);
	if (fd == -1) {
		i_error("socket() failed: %m");
		return -1;
	}

	sin_set_ip(&so, ip);
	if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
		close_keep_errno(fd);
		return -1;
	}
	(void)close(fd);
	return 0;
}

int net_connect_unix(const char *path)
{
	union sockaddr_union_unix sa;
	int fd, ret;

	memset(&sa, 0, sizeof(sa));
	sa.un.sun_family = AF_UNIX;
	if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
		/* too long path */
		errno = EINVAL;
		return -1;
	}

	/* create the socket */
	fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if (fd == -1) {
		i_error("socket(%s) failed: %m", path);
		return -1;
	}

	net_set_nonblock(fd, TRUE);<commit_msg>net_connect_unix(): If path is too long, return ENAMETOOLONG if it's available.<commit_after>/* Copyright (c) 1999-2012 Dovecot authors, see the included COPYING file */

#define _GNU_SOURCE /* For Linux's struct ucred */
#include "lib.h"
#include "close-keep-errno.h"
#include "fd-set-nonblock.h"
#include "time-util.h"
#include "network.h"

#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/un.h>
#include <netinet/tcp.h>
#ifdef HAVE_UCRED_H
#  include <ucred.h> /* for getpeerucred() */
#endif

union sockaddr_union {
	struct sockaddr sa;
	struct sockaddr_in sin;
#ifdef HAVE_IPV6
	struct sockaddr_in6 sin6;
#endif
};

union sockaddr_union_unix {
	struct sockaddr sa;
	struct sockaddr_un un;
};

#ifdef HAVE_IPV6
#  define SIZEOF_SOCKADDR(so) ((so).sa.sa_family == AF_INET6 ? \
	sizeof(so.sin6) : sizeof(so.sin))
#else
#  define SIZEOF_SOCKADDR(so) (sizeof(so.sin))
#endif

bool net_ip_compare(const struct ip_addr *ip1, const struct ip_addr *ip2)
{
	return net_ip_cmp(ip1, ip2) == 0;
}

int net_ip_cmp(const struct ip_addr *ip1, const struct ip_addr *ip2)
{
	if (ip1->family != ip2->family)
		return ip1->family - ip2->family;

#ifdef HAVE_IPV6
	if (ip1->family == AF_INET6)
		return memcmp(&ip1->u.ip6, &ip2->u.ip6, sizeof(ip1->u.ip6));
#endif

	return memcmp(&ip1->u.ip4, &ip2->u.ip4, sizeof(ip1->u.ip4));
}

unsigned int net_ip_hash(const struct ip_addr *ip)
{
        const unsigned char *p;
	unsigned int len, g, h = 0;

#ifdef HAVE_IPV6
	if (ip->family == AF_INET6) {
		p = ip->u.ip6.s6_addr;
		len = sizeof(ip->u.ip6);
	} else
#endif
	{
		return ip->u.ip4.s_addr;
	}

	for (; len > 0; len--, p++) {
		h = (h << 4) + *p;
		if ((g = h & 0xf0000000UL)) {
			h = h ^ (g >> 24);
			h = h ^ g;
		}
	}

	return h;
}

/* copy IP to sockaddr */
static inline void
sin_set_ip(union sockaddr_union *so, const struct ip_addr *ip)
{
	if (ip == NULL) {
#ifdef HAVE_IPV6
		so->sin6.sin6_family = AF_INET6;
		so->sin6.sin6_addr = in6addr_any;
#else
		so->sin.sin_family = AF_INET;
		so->sin.sin_addr.s_addr = INADDR_ANY;
#endif
		return;
	}

	so->sin.sin_family = ip->family;
#ifdef HAVE_IPV6
	if (ip->family == AF_INET6)
		memcpy(&so->sin6.sin6_addr, &ip->u.ip6, sizeof(ip->u.ip6));
	else
#endif
		memcpy(&so->sin.sin_addr, &ip->u.ip4, sizeof(ip->u.ip4));
}

static inline void
sin_get_ip(const union sockaddr_union *so, struct ip_addr *ip)
{
	/* IP structs may be sent across processes. Clear the whole struct
	   first to make sure it won't leak any data across processes. */
	memset(ip, 0, sizeof(*ip));

	ip->family = so->sin.sin_family;

#ifdef HAVE_IPV6
	if (ip->family == AF_INET6)
		memcpy(&ip->u.ip6, &so->sin6.sin6_addr, sizeof(ip->u.ip6));
	else
#endif
	if (ip->family == AF_INET)
		memcpy(&ip->u.ip4, &so->sin.sin_addr, sizeof(ip->u.ip4));
	else
		memset(&ip->u, 0, sizeof(ip->u));
}

static inline void sin_set_port(union sockaddr_union *so, unsigned int port)
{
#ifdef HAVE_IPV6
	if (so->sin.sin_family == AF_INET6)
                so->sin6.sin6_port = htons((unsigned short) port);
	else
#endif
		so->sin.sin_port = htons((unsigned short) port);
}

static inline unsigned int sin_get_port(union sockaddr_union *so)
{
#ifdef HAVE_IPV6
	if (so->sin.sin_family == AF_INET6)
		return ntohs(so->sin6.sin6_port);
#endif
	if (so->sin.sin_family == AF_INET)
		return ntohs(so->sin.sin_port);

	return 0;
}

#ifdef __FreeBSD__
static int
net_connect_ip_full_freebsd(const struct ip_addr *ip, unsigned int port,
			    const struct ip_addr *my_ip, bool blocking);

static int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
			       const struct ip_addr *my_ip, bool blocking)
{
	int fd, try;

	for (try = 0;;) {
		fd = net_connect_ip_full_freebsd(ip, port, my_ip, blocking);
		if (fd != -1 || ++try == 5 ||
		    (errno != EADDRINUSE && errno != EACCES))
			break;
		/*
		   This may be just a temporary problem:

		   EADDRINUSE: busy
		   EACCES: pf may cause this if another connection used
		           the same port recently
		*/
	}
	return fd;
}
/* then some kludging: */
#define net_connect_ip_full net_connect_ip_full_freebsd
#endif

static int net_connect_ip_full(const struct ip_addr *ip, unsigned int port,
			       const struct ip_addr *my_ip, bool blocking)
{
	union sockaddr_union so;
	int fd, ret, opt = 1;

	if (my_ip != NULL && ip->family != my_ip->family) {
		i_warning("net_connect_ip(): ip->family != my_ip->family");
                my_ip = NULL;
	}

	/* create the socket */
	memset(&so, 0, sizeof(so));
        so.sin.sin_family = ip->family;
	fd = socket(ip->family, SOCK_STREAM, 0);

	if (fd == -1) {
		i_error("socket() failed: %m");
		return -1;
	}

	/* set socket options */
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
	setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt));
	if (!blocking)
		net_set_nonblock(fd, TRUE);

	/* set our own address */
	if (my_ip != NULL) {
		sin_set_ip(&so, my_ip);
		if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
			i_error("bind(%s) failed: %m", net_ip2addr(my_ip));
			close_keep_errno(fd);
			return -1;
		}
	}

	/* connect */
	sin_set_ip(&so, ip);
	sin_set_port(&so, port);
	ret = connect(fd, &so.sa, SIZEOF_SOCKADDR(so));

#ifndef WIN32
	if (ret < 0 && errno != EINPROGRESS)
#else
	if (ret < 0 && WSAGetLastError() != WSAEWOULDBLOCK)
#endif
	{
                close_keep_errno(fd);
		return -1;
	}

	return fd;
}
#ifdef __FreeBSD__
#  undef net_connect_ip_full
#endif

int net_connect_ip(const struct ip_addr *ip, unsigned int port,
		   const struct ip_addr *my_ip)
{
	return net_connect_ip_full(ip, port, my_ip, FALSE);
}

int net_connect_ip_blocking(const struct ip_addr *ip, unsigned int port,
			    const struct ip_addr *my_ip)
{
	return net_connect_ip_full(ip, port, my_ip, TRUE);
}

int net_try_bind(const struct ip_addr *ip)
{
	union sockaddr_union so;
	int fd;

	/* create the socket */
	memset(&so, 0, sizeof(so));
        so.sin.sin_family = ip->family;
	fd = socket(ip->family, SOCK_STREAM, 0);
	if (fd == -1) {
		i_error("socket() failed: %m");
		return -1;
	}

	sin_set_ip(&so, ip);
	if (bind(fd, &so.sa, SIZEOF_SOCKADDR(so)) == -1) {
		close_keep_errno(fd);
		return -1;
	}
	(void)close(fd);
	return 0;
}

int net_connect_unix(const char *path)
{
	union sockaddr_union_unix sa;
	int fd, ret;

	memset(&sa, 0, sizeof(sa));
	sa.un.sun_family = AF_UNIX;
	if (i_strocpy(sa.un.sun_path, path, sizeof(sa.un.sun_path)) < 0) {
		/* too long path */
#ifdef ENAMETOOLONG
		errno = ENAMETOOLONG;
#else
		errno = EINVAL;
#endif
		return -1;
	}

	/* create the socket */
	fd = socket(PF_UNIX, SOCK_STREAM, 0);
	if (fd == -1) {
		i_error("socket(%s) failed: %m", path);
		return -1;
	}

	net_set_nonblock(fd, TRUE);
{'fraction_non_alphanumeric': 0.15372400756143667, 'fraction_numerical': 0.018449905482041586, 'mean_word_length': 3.7558432218626394, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5855340', 'n_tokens_mistral': 5737, 'n_tokens_neox': 5393, 'n_words': 1319}