net.c revision 8bb311aef8b8a6ddda990414083cecedcea77c09
/* Copyright (c) 1999-2016 Dovecot authors, see the included COPYING file */
#define _GNU_SOURCE /* For Linux's struct ucred */
#include "lib.h"
#include "fd-set-nonblock.h"
#include "time-util.h"
#include "net.h"
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#if defined(HAVE_UCRED_H)
# include <ucred.h> /* for getpeerucred() */
#elif defined(HAVE_SYS_UCRED_H)
#endif
union sockaddr_union {
struct sockaddr_in sin;
#ifdef HAVE_IPV6
struct sockaddr_in6 sin6;
#endif
};
union sockaddr_union_unix {
struct sockaddr_un un;
};
#ifdef HAVE_IPV6
#else
#endif
#if !defined(HAVE_GETPEEREID) && !defined(SO_PEERCRED) && !defined(HAVE_GETPEERUCRED) && defined(MSG_WAITALL) && defined(LOCAL_CREDS)
# define NEEDS_LOCAL_CREDS 1
#endif
{
}
{
#ifdef HAVE_IPV6
#endif
}
{
const unsigned char *p;
unsigned int len, g, h = 0;
#ifdef HAVE_IPV6
} else
#endif
{
}
h = (h << 4) + *p;
if ((g = h & 0xf0000000UL)) {
h = h ^ (g >> 24);
h = h ^ g;
}
}
return h;
}
/* copy IP to sockaddr */
static inline void
{
#ifdef HAVE_IPV6
#else
#endif
return;
}
#ifdef HAVE_IPV6
else
#endif
}
static inline void
{
/* IP structs may be sent across processes. Clear the whole struct
first to make sure it won't leak any data across processes. */
#ifdef HAVE_IPV6
else
#endif
else
}
{
#ifdef HAVE_IPV6
else
#endif
}
{
#ifdef HAVE_IPV6
#endif
return 0;
}
#ifdef __FreeBSD__
static int
bool blocking);
bool blocking)
{
for (try = 0;;) {
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: */
#endif
{
union sockaddr_union so;
i_warning("net_connect_ip(): ip->family != my_ip->family");
}
/* create the socket */
if (fd == -1) {
i_error("socket() failed: %m");
return -1;
}
/* set socket options */
if (sock_type == SOCK_STREAM)
if (!blocking)
/* set our own address */
i_close_fd(&fd);
return -1;
}
}
/* connect */
#ifndef WIN32
#else
#endif
{
i_close_fd(&fd);
return -1;
}
return fd;
}
#ifdef __FreeBSD__
#endif
{
}
{
}
{
}
{
union sockaddr_union so;
int fd;
/* create the socket */
if (fd == -1) {
i_error("socket() failed: %m");
return -1;
}
i_close_fd(&fd);
return -1;
}
i_close_fd(&fd);
return 0;
}
int net_connect_unix(const char *path)
{
union sockaddr_union_unix sa;
/* too long path */
#ifdef ENAMETOOLONG
#else
#endif
return -1;
}
/* create the socket */
if (fd == -1) {
return -1;
}
/* connect */
i_close_fd(&fd);
return -1;
}
#ifdef NEEDS_LOCAL_CREDS
{
int on = 1;
i_error("setsockopt(LOCAL_CREDS) failed: %m");
return -1;
}
}
#endif
return fd;
}
{
int fd;
i_panic("gettimeofday() failed: %m");
do {
break;
/* busy. wait for a while. */
i_panic("gettimeofday() failed: %m");
return fd;
}
void net_disconnect(int fd)
{
/* FreeBSD's close() fails with ECONNRESET if socket still has unsent
data in transmit buffer. We don't care. */
i_error("net_disconnect() failed: %m");
}
{
}
{
#ifdef TCP_CORK
#else
errno = ENOPROTOOPT;
return -1;
#endif
}
{
}
{
#ifdef HAVE_IPV6
#else
#endif
}
{
enum net_listen_flags flags = 0;
}
{
union sockaddr_union so;
/* create the socket */
#ifdef HAVE_IPV6
/* IPv6 is not supported by OS */
}
#endif
if (fd == -1) {
i_error("socket() failed: %m");
return -1;
}
/* set socket options */
if ((*flags & NET_LISTEN_FLAG_REUSEPORT) != 0) {
#ifdef SO_REUSEPORT
#endif
}
/* If using IPv6, bind only to IPv6 if possible. This avoids
ambiguities with IPv4-mapped IPv6 addresses. */
#ifdef IPV6_V6ONLY
opt = 1;
}
#endif
if (ret < 0) {
if (errno != EADDRINUSE) {
i_error("bind(%s, %u) failed: %m",
}
} else {
/* get the actual port we started listen */
if (ret >= 0) {
/* start listening */
return fd;
if (errno != EADDRINUSE)
i_error("listen() failed: %m");
}
}
/* error */
i_close_fd(&fd);
return -1;
}
{
union {
struct sockaddr_un un;
} sa;
int fd;
/* too long path */
return -1;
}
/* create the socket */
if (fd == -1) {
i_error("socket() failed: %m");
return -1;
}
#ifdef NEEDS_LOCAL_CREDS
{
int on = 1;
i_error("setsockopt(LOCAL_CREDS) failed: %m");
return -1;
}
}
#endif
/* bind */
if (errno != EADDRINUSE)
} else {
/* start listening */
return fd;
if (errno != EADDRINUSE)
i_error("listen() failed: %m");
}
i_close_fd(&fd);
return -1;
}
{
unsigned int i = 0;
int fd;
return -1;
/* see if it really exists */
errno = EADDRINUSE;
return -1;
}
/* delete and try again */
if (i_unlink_if_exists(path) < 0) {
errno = EADDRINUSE;
return -1;
}
}
return fd;
}
{
union sockaddr_union so;
int ret;
if (ret < 0) {
return -1;
else
return -2;
}
} else {
}
return ret;
}
{
if (ret == 0) {
/* disconnected */
errno = 0;
return -2;
}
return 0;
/* treat as disconnection */
return -2;
}
}
return ret;
}
{
if (ret == -1) {
return 0;
return -2;
}
return ret;
}
unsigned int *ips_count)
{
/* @UNSAFE */
#ifdef HAVE_IPV6
union sockaddr_union *so;
int host_error;
#else
#endif
int count;
*ips_count = 0;
#ifdef HAVE_IPV6
/* support [ipv6] style addresses here so they work globally */
*ips_count = 1;
return 0;
}
/* save error to host_error for later use */
if (host_error != 0)
return host_error;
/* get number of IPs */
count++;
count = 0;
}
#else
return h_errno;
/* get number of IPs */
count = 0;
count++;
while (count > 0) {
count--;
}
#endif
return 0;
}
{
union sockaddr_union so;
char hbuf[NI_MAXHOST];
int ret;
if (ret != 0)
return ret;
return 0;
}
{
union sockaddr_union so;
return -1;
} else {
}
return 0;
}
{
union sockaddr_union so;
return -1;
} else {
}
return 0;
}
{
union sockaddr_union_unix so;
return -1;
return -1;
}
return 0;
}
{
#if defined(SO_PEERCRED)
# if defined(HAVE_STRUCT_SOCKPEERCRED)
/* OpenBSD (may also provide getpeereid, but we also want pid) */
struct sockpeercred ucred;
# else
/* Linux */
# endif
i_error("getsockopt(SO_PEERCRED) failed: %m");
return -1;
}
return 0;
#elif defined(LOCAL_PEEREID)
/* NetBSD (may also provide getpeereid, but we also want pid) */
i_error("getsockopt(LOCAL_PEEREID) failed: %m");
return -1;
}
return 0;
#elif defined(HAVE_GETPEEREID)
/* OSX 10.4+, FreeBSD 4.6+, OpenBSD 3.0+, NetBSD 5.0+ */
i_error("getpeereid() failed: %m");
return -1;
}
return 0;
#elif defined(LOCAL_PEERCRED)
/* Older FreeBSD */
i_error("getsockopt(LOCAL_PEERCRED) failed: %m");
return -1;
}
return -1;
}
return 0;
#elif defined(HAVE_GETPEERUCRED)
/* Solaris */
i_error("getpeerucred() failed: %m");
return -1;
}
return -1;
}
return 0;
/* NetBSD < 5 */
int i, n, on;
struct {
char buf[110];
} cdata;
for (i = 0; i < 10; i++) {
break;
usleep(100);
}
if (n < 0) {
i_error("recvmsg() failed: %m");
return -1;
}
return 0;
#else
return -1;
#endif
}
{
#ifdef HAVE_IPV6
return "";
#else
unsigned long ip4;
return "";
return t_strdup_printf("%lu.%lu.%lu.%lu",
(ip4 & 0x000000ff));
#endif
}
{
int ret;
/* IPv6 */
#ifdef HAVE_IPV6
T_BEGIN {
if (addr[0] == '[') {
/* allow [ipv6 addr] */
}
} T_END;
if (ret == 0)
return -1;
#else
#endif
} else {
/* IPv4 */
return -1;
}
return 0;
}
{
uintmax_t l;
if (str_to_uintmax(str, &l) < 0)
return -1;
if (l == 0 || l > (in_port_t)-1)
return -1;
return 0;
}
{
uintmax_t l;
if (str_to_uintmax(str, &l) < 0)
return -1;
if (l > (in_port_t)-1)
return -1;
return 0;
}
{
const char *p, *host;
if (str[0] == '[') {
/* [IPv6] address, possibly followed by :port */
if (p == NULL)
return -1;
} else {
/* host or IPv6 address */
*port_r = default_port;
return 0;
}
}
if (p[0] == '\0') {
*port_r = default_port;
return 0;
}
if (p[0] != ':')
return -1;
return -1;
return 0;
}
{
#ifdef HAVE_IPV6
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff };
if (!IPADDR_IS_V6(src))
return -1;
return -1;
return 0;
#else
return -1;
#endif
}
int net_geterror(int fd)
{
int data;
/* we're now really returning the getsockopt()'s error code
instead of the socket's, but normally we should never get
here anyway. */
return errno;
}
return data;
}
const char *net_gethosterror(int error)
{
#ifdef HAVE_IPV6
return gai_strerror(error);
#else
switch (error) {
case HOST_NOT_FOUND:
return "Host not found";
case NO_ADDRESS:
return "No IP address found for name";
case NO_RECOVERY:
return "A non-recoverable name server error occurred";
case TRY_AGAIN:
return "A temporary error on an authoritative name server";
default:
}
#endif
}
int net_hosterror_notfound(int error)
{
#ifdef HAVE_IPV6
#ifdef EAI_NODATA /* NODATA is depricated */
#else
#endif
#else
#endif
}
{
}
bool is_ipv4_address(const char *addr)
{
while (*addr != '\0') {
return FALSE;
addr++;
}
return TRUE;
}
bool is_ipv6_address(const char *addr)
{
bool have_prefix = FALSE;
if (*addr == '[') {
have_prefix = TRUE;
addr++;
}
while (*addr != '\0') {
break;
return FALSE;
}
addr++;
}
return TRUE;
}
unsigned int *bits_r)
{
const char *p;
if (p != NULL)
return -1;
if (p == NULL) {
/* full IP address must match */
} else {
/* get the network mask */
return -1;
}
return 0;
}
{
unsigned int pos, i;
/* IPv4 address mapped disguised as IPv6 address */
}
anything */
return FALSE;
}
/* one is IPv6 and one is IPv4 */
return FALSE;
}
if (IPADDR_IS_V4(ip)) {
} else {
#ifdef HAVE_IPV6
#else
/* shouldn't get here */
return FALSE;
#endif
}
/* check first the full 32bit ints */
return FALSE;
}
/* check the last full bytes */
return FALSE;
}
/* check the last bits, they're reversed in bytes */
return FALSE;
}
return TRUE;
}