/* Distributed Checksum Clearinghouse
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.21 $Revision$
 */

#include "dcc_defs.h"
#ifndef DCC_WIN32
#include <arpa/inet.h>
#endif


/* this is not thread safe */

/* Convert IP address to a string, but not into a single buffer
 *	This is not thread safe, but it can be used in printf()s */
const char *
dcc_su2str(const DCC_SOCKU *su)
{
	static int bufno;
	static struct {
	    char    str[INET6_ADDRSTRLEN+1+6+1];
	} bufs[4];
	u_short port;
	char *s;

	s = bufs[bufno].str;
	bufno = (bufno+1) % DIM(bufs);

	if (su->sa.sa_family == AF_INET) {
		if (!DCC_INET_NTOP(AF_INET, &su->ipv4.sin_addr,
				   s, sizeof(bufs[0].str)))
			strcpy(s, "???");
		port = su->ipv4.sin_port;
	} else {
		if (!DCC_INET_NTOP(su->sa.sa_family, &su->ipv6.sin6_addr,
				   s, sizeof(bufs[0].str)))
			strcpy(s, "???");
		port = su->ipv6.sin6_port;
	}
	sprintf(s+strlen(s), ",%d", ntohs(port));
	return s;
}



/* Convert IP address and optional port number to a string,
 * but not into a single buffer
 *	This is not thread safe, but it can be used in debugging printf()s */
const char *
dcc_su2str_opt(const DCC_SOCKU *su,
	   u_int16_t def_port,
	   char port_str)		/* 0, '\0' or '-' */
{
	static int bufno;
	static struct {
	    char    str[INET6_ADDRSTRLEN+1+6+1];
	} bufs[4];
	char *s, *s1;

	s = bufs[bufno].str;
	bufno = (bufno+1) % DIM(bufs);

	if (su->sa.sa_family == AF_INET) {
		if (!DCC_INET_NTOP(su->sa.sa_family, &su->ipv4.sin_addr,
				   s, sizeof(bufs[0].str)))
			strcpy(s, "???");
	} else {
		if (!DCC_INET_NTOP(su->sa.sa_family, &su->ipv6.sin6_addr,
				   s, sizeof(bufs[0].str)))
			strcpy(s, "???");
	}
	s1 = s+strlen(s);
	if (su->ipv4.sin_port == def_port || def_port == 0) {
		if (port_str) {
			*s1 = ',';
			*++s1 = port_str;
			*++s1 = '\0';
		}
	} else {
		sprintf(s1, ",%d", ntohs(su->ipv4.sin_port));
	}

	return s;
}



/* Convert IP address to a name
 *	This function is not thread safe */
void
dcc_su2name(char *name, int name_len, const DCC_SOCKU *su)
{
#ifdef NO_IPV6
	struct hostent *hp;

	if (su->sa.sa_family == AF_INET) {
		hp = gethostbyaddr((char*)&su->ipv4.sin_addr,
				   sizeof(su->ipv4.sin_addr),
				   AF_INET);
	} else if (su->sa.sa_family == AF_INET6
		   && su->ipv6.sin6_addr.s6_addr32[2] == htonl(0xffff)) {
		hp = gethostbyaddr((char*)&su->ipv6.sin6_addr.s6_addr32[3],
				   sizeof(su->ipv6.sin6_addr.s6_addr32[3]),
				   AF_INET);
	} else {
		hp = 0;
	}
	if (!hp) {
		if (name_len > 0)
			*name = '\0';
		return;
	}
	if (name_len > 0) {
		if (name_len > 1)
			strncpy(name, hp->h_name, name_len);
		name[name_len-1] = '\0';
	}
#endif
#ifdef USE_GETIPNODEBYNAME
	struct hostent *hp;
	int error;

	if (su->sa.sa_family == AF_INET)
		hp = getipnodebyaddr(&su->ipv4.sin_addr,
				     sizeof(su->ipv4.sin_addr),
				     su->sa.sa_family, &error);
	else
		hp = getipnodebyaddr(&su->ipv6.sin6_addr,
				     sizeof(su->ipv6.sin6_addr),
				     su->sa.sa_family, &error);
	if (!hp) {
		if (name_len > 0)
			*name = '\0';
		return;
	}
	if (name_len > 0) {
		if (name_len > 1)
			strncpy(name, hp->h_name, name_len);
		name[name_len-1] = '\0';
	}

	freehostent(hp);
#endif
#ifdef USE_GETADDRINFO
	if (su->sa.sa_family == AF_INET)
		getnameinfo((struct sockaddr *)&su->ipv4.sin_addr,
			    sizeof(su->ipv4.sin_addr),
			    name, name_len,
			    0, 0, NI_NAMEREQD);
	else
		getnameinfo((struct sockaddr *)&su->ipv6.sin6_addr,
			    sizeof(su->ipv6.sin6_addr),
			    name, name_len,
			    0, 0, NI_NAMEREQD);
#endif
}
