/* Distributed Checksum Clearinghouse
 *
 * sockets and IP addresses
 *
 * --C-LICENSE--
 * $Revision: 1.34 $
 */

#ifndef DCC_SOCKET_H
#define DCC_SOCKET_H

#ifdef DCC_USE_POLL
#include <poll.h>
#endif


#define DCC_MAXDOMAINLEN   256		/* limit host names; with \0 */

/* we need IPv6 addresses even without IPv6 support
 *	defend against lurking definitions */
#ifdef DCC_NO_IPV6
#undef in6_addr
#define in6_addr dcc_in6_addr
#undef s6_addr32
#define s6_addr32 __u6_addr.dcc_s6_addr32
#undef s6_addr
#define s6_addr __u6_addr.dcc_s6_addr
#undef sockaddr_in6
#define sockaddr_in6 dcc_sockaddr_in6
#endif /* DCC_NO_IPV6 */

/* 4.4BSD sockets */
#ifdef HAVE_SA_LEN
#define DCC_SU_LEN(s) ((s)->sa.sa_len)
#else
#define DCC_SU_LEN(s) (((s)->sa.sa_family == AF_INET)	\
		       ? sizeof((s)->ipv4) : sizeof((s)->ipv6))
#endif

#ifndef HAVE_SOCKLEN_T
typedef u_int32_t socklen_t;
#endif
#ifndef HAVE_IN_ADDR_T
typedef u_int32_t in_addr_t;
#endif
#ifndef HAVE_SA_FAMILY_T
typedef u_char sa_family_t;
#endif
#ifndef HAVE_IN_PORT_T
typedef u_int16_t in_port_t;
#endif

#ifdef DCC_UNIX
typedef int DCC_SOCKET;
#else
typedef SOCKET DCC_SOCKET;
#endif


#if !defined(HAVE_AF_LOCAL) && !defined(AF_LOCAL)
#define AF_LOCAL AF_UNIX
#endif


/* These are needed even when IPv6 is available to handle old map file
 *	formats in dcc_clnt.h */
struct dcc_in6_addr {
    union {
	u_int8_t    dcc_s6_addr[16];
	u_int32_t   dcc_s6_addr32[4];
    } __u6_addr;
};
typedef u_int32_t DCC_SCOPE_ID;
struct dcc_sockaddr_in6 {
    u_char	sin6_len;
    sa_family_t	sin6_family;
    in_port_t	sin6_port;
    u_int32_t	sin6_flowinfo;
    struct dcc_in6_addr sin6_addr;
    DCC_SCOPE_ID sin6_scope_id;
};

/* puzzle out something for the non-standard and often hidden s6_addr32 */
#if !defined(s6_addr32) && defined(DCC_CONF_S6_ADDR32)
#define s6_addr32 DCC_CONF_S6_ADDR32
#endif

typedef struct {
    struct in6_addr lo;
    struct in6_addr hi;
} DCC_IP_RANGE;

typedef union {
    struct sockaddr sa;
    struct sockaddr_in ipv4;
    struct sockaddr_in6 ipv6;
} DCC_SOCKU;

/* Some Linux uses 16 bits for sa_family_t */
typedef	u_int16_t DCC_IP_FAMILY;

/* we cannot always use DCC_SOCKU because sizeof(sockaddr_in6) is not defined
 * on systems without IPv6 and when they do get IPv6, the native sockaddr_in
 * often differs in size from the work-around version in the DCC source */
typedef struct {
    union {
	struct in_addr ipv4;
	struct in6_addr ipv6;
    } u;
    DCC_SCOPE_ID scope_id;
    in_port_t	port;
    DCC_IP_FAMILY family;
} DCC_IP;

#if !defined(HAVE_AF_INET6) && !defined(AF_INET6)
#define AF_INET6 24
#endif

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46		/* including terminal '\0' */
#endif
#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16		/* including terminal '\0' */
#endif

#define DCC_IPV4_CHARS	".0123456789"
#define DCC_IPV6_CHARS	DCC_IPV4_CHARS":abcdefABCDEF"

#define DCC_SU_IS_LINKLOCAL(s) ((s)->sa.sa_family == AF_INET6		    \
				&& (s)->ipv6.sin6_addr.s6_addr[0] == 0xfe   \
				&& (((s)->ipv6.sin6_addr.s6_addr[1] & 0xc0) \
				    == 0x80))
#define DCC_SU_IS_SITELOCAL(s) ((s)->sa.sa_family == AF_INET6		    \
				&& (s)->ipv6.sin6_addr.s6_addr[0] == 0xfe   \
				&& (((s)->ipv6.sin6_addr.s6_addr[1] & 0xc0) \
				    == 0xc0))


#define DCC_IPV6_ALIT	"IPv6:"		/* address literal prefix */

#define MAXPORTNAMELEN	64

#ifndef INADDR_LOOPBACK
#define	INADDR_LOOPBACK	(u_int32_t)0x7f000001
#endif
#ifndef INADDR_NONE
#define INADDR_NONE	0xffffffff
#endif


/* l-value
 *	use IPv4 port number if sa_family is AF_UNSPEC */
#define DCC_SU_PORT(su) (*((su)->sa.sa_family == AF_INET6		\
			   ? &(su)->ipv6.sin6_port			\
			   : &(su)->ipv4.sin_port))

#define DCC_ADDR6_V4MAPPED(ap) ((ap)->s6_addr32[0] == 0			\
				&& (ap)->s6_addr32[1] == 0		\
				&& (ap)->s6_addr32[2] == ntohl(0x0000ffff))

#define DCC_ADDR6_IS_ANY(ap) ((ap)->s6_addr32[0] == 0			\
			     && (ap)->s6_addr32[1] == 0			\
			     && (ap)->s6_addr32[2] == 0			\
			     && (ap)->s6_addr32[3] == 0)

#define DCC_SU_IS_ANY(su) ((su)->sa.sa_family == AF_INET		\
			   ? (su)->ipv4.sin_addr.s_addr == INADDR_ANY	\
			   : (su)->sa.sa_family == AF_INET6		\
			   ? DCC_ADDR6_IS_ANY(&(su)->ipv6.sin6_addr)	\
			   : 0)

/* zero port number if sa_family is AF_UNSPEC */
#define DCC_SU_PORT_Z(su) ((su)->sa.sa_family == AF_INET		\
			   ? (su)->ipv4.sin_port			\
			   : (su)->sa.sa_family == AF_INET6		\
			   ? (su)->ipv6.sin6_port			\
			   : 0)

#ifdef DCC_NO_IPV6
#undef HAVE_SIN6_SCOPE_ID
#endif

#ifdef HAVE_SIN6_SCOPE_ID
#define _DCC_IPV6_EQ(a1,s1,a2,s2) (!memcmp((a1),(a2), sizeof(struct in6_addr)) \
				   && (s1) == (s2))
#else
#define _DCC_IPV6_EQ(a1,s1,a2,s2) (!memcmp((a1),(a2), sizeof(struct in6_addr)))
#endif /* HAVE_SIN6_SCOPE_ID */
#ifdef HAVE_GCC_INLINE			   /* no prototypes without inline */
static inline u_char
DCC_IPV6_EQ(const struct in6_addr *a1, u_int32_t s1 DCC_UNUSED,
	    const struct in6_addr *a2, u_int32_t s2 DCC_UNUSED)
{ return _DCC_IPV6_EQ(a1,s1,a2,s2); }
#else
#define DCC_IPV6_EQ(a1,s1,a2,s2) _DCC_IPV6_EQ(a1,s1,a2,s2)
#endif

/* compare struc in6_addr */
#define _DCC_SU_IPV6_EQ(a,b) DCC_IPV6_EQ(&(a)->sin6_addr, (a)->sin6_scope_id, \
					 &(b)->sin6_addr, (b)->sin6_scope_id)

/* compare DCC_SOCKU addresses but not port numbers */
#define _DCC_SUnP_EQ(a,b) ((a)->sa.sa_family == (b)->sa.sa_family	\
			   && (((a)->sa.sa_family == AF_INET)		\
			       ? ((a)->ipv4.sin_addr.s_addr		\
				  == (b)->ipv4.sin_addr.s_addr)		\
			       : _DCC_SU_IPV6_EQ(&(a)->ipv6, &(b)->ipv6)))
#ifdef HAVE_GCC_INLINE			   /* no prototypes without inline */
static inline u_char
DCC_SUnP_EQ(const DCC_SOCKU *a, const DCC_SOCKU *b)
{ return _DCC_SUnP_EQ(a,b); }
#else
#define DCC_SUnP_EQ(a,b) _DCC_SUnP_EQ(a,b)
#endif

/* compare DCC_SOCKU to DCC_IP but not port numbers */
#define _DCC_SUnP_IP_EQ(s,i) ((s)->sa.sa_family == (i)->family		\
			     && (((s)->sa.sa_family == AF_INET)		\
				 ? ((s)->ipv4.sin_addr.s_addr		\
				    == (i)->u.ipv4.s_addr)		\
				 : DCC_IPV6_EQ(&(s)->ipv6.sin6_addr,	\
					       (s)->ipv6.sin6_scope_id,	\
					       &(i)->u.ipv6,		\
					       (i)->scope_id)))
#ifdef HAVE_GCC_INLINE			   /* no prototypes without inline */
static inline u_char
DCC_SUnP_IP_EQ(const DCC_SOCKU *s, const DCC_IP *i)
{
	return _DCC_SUnP_IP_EQ(s,i);
}
#else
#define DCC_SUnP_IP_EQ(s,i) _DCC_SUnP_IP_EQ(s,i)
#endif

/* compare DCC_SOCKU addresses */
#define _DCC_SU_EQ(a,b) (DCC_SU_PORT(a) == DCC_SU_PORT(b) && DCC_SUnP_EQ(a,b))
#ifdef HAVE_GCC_INLINE			   /* no prototypes without inline */
static inline u_char
DCC_SU_EQ(const DCC_SOCKU *a, const DCC_SOCKU *b) { return _DCC_SU_EQ(a,b); }
#else
#define DCC_SU_EQ(a,b) _DCC_SU_EQ(a,b)
#endif

/* compare DCC_SOCKU to DCC_IP */
#define _DCC_SU_IP_EQ(s,i) (DCC_SU_PORT(s) == (i)->port && _DCC_SUnP_IP_EQ(s,i))
#ifdef HAVE_GCC_INLINE			   /* no prototypes without inline */
static inline u_char
DCC_SU_IP_EQ(const DCC_SOCKU *s, const DCC_IP *i) { return _DCC_SU_IP_EQ(s,i); }
#else
#define DCC_SU_IP_EQ(s,i) _DCC_SU_IP_EQ(s,i)
#endif

#if !defined(HAVE_INET_NTOP) || defined(DCC_NO_IPV6)
#define DCC_INET_NTOP dcc_inet_ntop
#else
#define DCC_INET_NTOP inet_ntop
#endif
extern const char *dcc_inet_ntop(int, const void *, char *, size_t);

#if !defined(HAVE_INET_PTON) || defined(DCC_NO_IPV6)
#define DCC_INET_PTON dcc_inet_pton
#else
#define DCC_INET_PTON inet_pton
#endif
extern int dcc_inet_pton(int, const char *, void *);


#ifdef DCC_UNIX
/* at least some filters including IPFW say EACCES on hits,
 * so treat EACCES like Unreachables */
#define UNREACHABLE_ERRORS() (errno == ECONNREFUSED			\
			      || errno == EHOSTUNREACH			\
			      || errno == ENETUNREACH			\
			      || errno == EHOSTDOWN			\
			      || errno == ENETDOWN			\
			      || errno == EACCES)

#define CONN_WAIT_ERRORS() (errno == EAGAIN || errno == EINPROGRESS	\
			    || errno == EALREADY)
#define CONN_ERRORS() (UNREACHABLE_ERRORS() || errno == EINVAL		\
		       || errno == ECONNABORTED || errno == ECONNRESET	\
		       || errno == ETIMEDOUT || errno == ECONNREFUSED)

#else /* !DCC_UNIX or DCC_WIN32 */
#define UNREACHABLE_ERRORS() (errno == WSAECONNREFUSED			\
			      || errno == WSAEHOSTUNREACH		\
			      || errno == WSAENETUNREACH		\
			      || errno == WSAEHOSTDOWN			\
			      || errno == WSAENETDOWN)
#define EADDRINUSE	WSAEADDRINUSE

#define MAXHOSTNAMELEN   256

/* Microsoft sendto() and recvfrom() want (char *) buffers */
#define WIN32_SOC_CAST (char *)

/* Microsoft expects ints where other systems want socklen_t, which is usually
 * unsigned */
typedef int32_t socklen_t;

#endif /* DCC_WIN32 */



extern void dcc_ipv4toipv6(struct in6_addr *, const struct in_addr);
extern u_char dcc_ipv6toipv4(struct in_addr *, const struct in6_addr *);
extern void dcc_su2ip(DCC_IP *, const DCC_SOCKU *);
extern u_char dcc_ipv6su2ipv4su(DCC_SOCKU *, const DCC_SOCKU *);
extern u_char dcc_ipv4su2ipv6su(DCC_SOCKU *, const DCC_SOCKU *);
extern const struct in6_addr *dcc_su2ipv6(struct in6_addr *, u_char,
					  const DCC_SOCKU *);

extern const char *dcc_trim_ffff(const char *);
#define DCC_SU2STR_SIZE (INET6_ADDRSTRLEN+1+6+1)
extern const char *dcc_ipv4tostr(char *, int, const struct in_addr *);
extern const char *dcc_ipv6tostr(char *, int, const struct in6_addr *);
extern const char *dcc_ipv6tostr2(char *, int, const struct in6_addr *);
extern const char *dcc_ip2str(char *, int, const DCC_IP *);
extern const char *dcc_su2str(char *, int, const DCC_SOCKU *);
extern const char *dcc_su2str2(char *, int, const DCC_SOCKU *);
extern const char *dcc_su2str3(char *, int, const DCC_SOCKU *, u_short);
extern const char *dcc_su2str_err(const DCC_SOCKU *);
extern const char *dcc_host_portname(char *, int, const char *, const char *);
extern const char *dcc_su2name(char *, int, const DCC_SOCKU *);


/* use very old fashioned gethostbyname() if we are not doing any IPv6 */
#ifdef DCC_NO_IPV6
#undef HAVE_GETIPNODEBYNAME
#undef HAVE_GETADDRINFO
#endif

#if !defined(HAVE_GETIPNODEBYNAME) || !defined(HAVE_FREEHOSTENT) || !defined(HAVE_GETIPNODEBYADDR)
#undef HAVE_GETIPNODEBYNAME
#endif

#if !defined(HAVE_GETADDRINFO) || !defined(HAVE_FREEADDRINFO) || !defined(HAVE_GAI_STRERROR) || !defined(HAVE_GETNAMEINFO)
#undef HAVE_GETADDRINFO
#endif

/* prefer getaddrinfo() for IPv6 resolution */
#ifdef HAVE_GETADDRINFO
#define DCC_USE_GETADDRINFO
#define DCC_GETHOSTBYNAME_NAME "getaddrinfo"
#endif
#if defined(HAVE_GETIPNODEBYNAME) && !defined(DCC_USE_GETADDRINFO)
#define DCC_USE_GETIPNODEBYNAME
#define DCC_GETHOSTBYNAME_NAME "getaddrinfo"
#endif
#if !defined(DCC_USE_GETIPNODEBYNAME) && !defined(DCC_USE_GETADDRINFO)
#define DCC_USE_GETHOSTBYNAME
#define DCC_GETHOSTBYNAME_NAME "gethostbyname"
#endif


#ifdef HAVE_HSTRERROR
#define DCC_HSTRERROR(e) hstrerror(e)
#else
#ifdef DCC_WIN32
#define DCC_HSTRERROR(e) ws_strerror(e)
#else
#define DCC_HSTRERROR(e) dcc_hstrerror(e)
#endif
#endif

/* deal with inconsistent error numbers between hsterror() and gai_strerror() */
#ifdef DCC_USE_GETADDRINFO
/* encode the small positive error numbers used in BSD style getaddrinfo()
 * and the small negative numbers used in Linux style getaddrinfo() */
#define DCC_GAI_ERRNO(e) ((e)+0x2000)
#define H_ERROR_STR1(e) (((unsigned)(e) >= (unsigned)0x1000)		\
			 ? gai_strerror((e)-0x2000)			\
			 : DCC_HSTRERROR(e))
#define DCC_HERRNO_TRY_AGAIN(e) ((e) == DCC_GAI_ERRNO(EAI_AGAIN)	\
				 || (e) == TRY_AGAIN)
#else /* !DCC_USE_GETADDRINFO */
#define H_ERROR_STR1(e) DCC_HSTRERROR(e)
#ifdef DCC_WIN32
#define DCC_HERRNO_TRY_AGAIN(e) ((e) == WSATRY_AGAIN)
#else
#define DCC_HERRNO_TRY_AGAIN(e) ((e) == TRY_AGAIN)
#endif
#endif /* !DCC_USE_GETADDRINFO */


extern u_char dcc_su_is_loopback(const DCC_SOCKU *);
extern u_int dcc_get_port(DCC_EMSG *, const char *, u_int, const char *, int);
#define DCC_GET_PORT_INVALID	0x10000
#define DCC_MAX_HOSTADDRS	20	/* must be > DCC_MAX_SRVR_ADDRS */
extern DCC_SOCKU dcc_hostaddrs[DCC_MAX_HOSTADDRS];
extern char dcc_host_canonname[DCC_MAXDOMAINLEN];
extern DCC_SOCKU *dcc_hostaddrs_end;
typedef enum {				/* gethostby*() preferences */
    DCC_GETH_V4 = 0,			/*    get IPv4 address */
    DCC_GETH_V6 = 1,			/*    get IPv6 */
    DCC_GETH_V64 = 2,			/*    both but prefer IPv6 */
    DCC_GETH_V46 = 3			/*    both but prefer IPv4 */
} DCC_GETH;
#define DCC_GETH_DEF DCC_GETH_V46
#define DCC_LOCALHOST		"@"	/* kludge for 127.1 and ::1 */
#define DCC_INADDRANY		"*"
extern u_char dcc_get_host(const char *, DCC_GETH, int *);
extern u_char dcc_get_host_SOCKS(const char *, DCC_GETH, int *);
extern const char *dcc_hstrerror(int);

extern DCC_SOCKU *dcc_mk_inet_su(DCC_SOCKU *,
				 const struct in_addr *, in_port_t);
extern DCC_SOCKU *dcc_mk_inet6_su(DCC_SOCKU *,
				  const struct in6_addr *, u_int32_t, in_port_t);
extern DCC_SOCKU *dcc_mk_su(DCC_SOCKU *, sa_family_t,
			    const void *, DCC_SCOPE_ID, in_port_t);
extern DCC_SOCKU *dcc_mk_loop_su(DCC_SOCKU *s, sa_family_t, in_port_t);
extern DCC_SOCKU *dcc_ip2su(DCC_SOCKU *, const DCC_IP *);

extern u_char soc_v6only(DCC_EMSG *, DCC_SOCKET, const DCC_SOCKU *);
extern u_char udp_create(DCC_EMSG *, DCC_SOCKET *, const DCC_SOCKU *,
			 u_char, u_char, int *);
extern u_char ipv_check(DCC_EMSG *, sa_family_t, u_char);
extern u_char set_rcvbuf(DCC_EMSG *, int, const DCC_SOCKU *, int *, u_char *);

extern u_int len_ip_range(const DCC_IP_RANGE *);
extern void inc_ip6(struct in6_addr *);
#define _IN_RANGE(t,r) (memcmp((t), &(r)->lo, sizeof(struct in6_addr)) >= 0  \
		       && memcmp((t), &(r)->hi, sizeof(struct in6_addr)) <= 0)
#ifdef HAVE_GCC_INLINE			/* no prototypes without inline */
static inline u_char
in_range(const struct in6_addr *tgt, const DCC_IP_RANGE *range)
{return _IN_RANGE(tgt,range);}
#else
#define in_range(t,r) _IN_RANGE(t,r)
#endif
extern u_char cidr2range(DCC_IP_RANGE *, const struct in6_addr *, int);
extern int str2range(DCC_EMSG *, DCC_IP_RANGE *, u_char *,
		     const char *, const char *, int);
extern const char *range2str(char *, int, const DCC_IP_RANGE *);
extern u_char str2su(DCC_SOCKU *, const char *);
extern void bits2mask(struct in6_addr *, int);
#define apply_ipmask(_r, _a, _m) (					\
	(_r)->s6_addr32[0] = (_a)->s6_addr32[0] & (_m)->s6_addr32[0],	\
	(_r)->s6_addr32[1] = (_a)->s6_addr32[1] & (_m)->s6_addr32[1],	\
	(_r)->s6_addr32[2] = (_a)->s6_addr32[2] & (_m)->s6_addr32[2],	\
	(_r)->s6_addr32[3] = (_a)->s6_addr32[3] & (_m)->s6_addr32[3])
extern int str2cidr(DCC_EMSG *, struct in6_addr *, struct in6_addr *,
		    const char *);

#ifdef DCC_USE_POLL
typedef struct pollfd DCC_POLLFD;
#ifndef INFTIM
#define INFTIM (-1)
#endif
#else
typedef struct {
#ifdef DCC_WIN32
    u_int   fd;
#else
    int	    fd;
#endif
    short   events;
    short   revents;
} DCC_POLLFD;
#endif
extern int select_poll(DCC_EMSG *, DCC_POLLFD *, int, u_char, int);


#ifdef HAVE_RSENDTO
extern int Rconnect(int, const struct sockaddr *, socklen_t);
extern ssize_t Rsend(int, const void *, size_t, int);
extern ssize_t Rsendto(int, const void *, size_t, int,
		       const struct sockaddr *, size_t);
extern ssize_t Rrecv(int, void *, size_t, int);
extern ssize_t Rrecvfrom(int, void *, size_t, int,
			 struct sockaddr *, socklen_t *);
extern struct hostent *Rgethostbyname(const char *);
#else
#define Rconnect    connect
#define Rsend	    send
#define Rsendto	    sendto
#define Rrecv	    recv
#define Rrecvfrom   recvfrom
#define Rgethostbyname gethostbyname
#endif


#endif /* DCC_SOCKET_H */
