/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * selfcheck.c
 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <errno.h>
#include <syslog.h>

#include <strings.h>
#include <malloc.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if.h>

int
self_check(char *hostname)
{
	int s, res = 0;
	struct sioc_addrreq areq;

	struct hostent *hostinfo;
	int family;
	int flags;
	int error_num;
	char **hostptr;

	struct sockaddr_in6 ipv6addr;

	family = AF_INET6;
	/*
	 * We cannot specify AI_DEFAULT since it includes AI_ADDRCONFIG.
	 * Localhost name resolution will fail if no IP interfaces other than
	 * loopback are plumbed and AI_ADDRCONFIG is specified, and this
	 * causes localhost mounts to fail.
	 */
	flags = AI_V4MAPPED;

	if ((s = socket(family, SOCK_DGRAM, 0)) < 0) {
		syslog(LOG_ERR, "self_check: socket: %m");
		return (0);
	}

	if ((hostinfo = getipnodebyname(hostname, family, flags,
	    &error_num)) == NULL) {

		if (error_num == TRY_AGAIN)
			syslog(LOG_DEBUG,
			    "self_check: unknown host: %s (try again later)\n",
			    hostname);
		else
			syslog(LOG_DEBUG,
			    "self_check: unknown host: %s\n", hostname);

		(void) close(s);
		return (0);
	}

	for (hostptr = hostinfo->h_addr_list; *hostptr; hostptr++) {
		bzero(&ipv6addr, sizeof (ipv6addr));
		ipv6addr.sin6_family = AF_INET6;
		ipv6addr.sin6_addr = *((struct in6_addr *)(*hostptr));
		memcpy(&areq.sa_addr, (void *)&ipv6addr, sizeof (ipv6addr));
		areq.sa_res = -1;
		(void) ioctl(s, SIOCTMYADDR, (caddr_t)&areq);
		if (areq.sa_res == 1) {
			res = 1;
			break;
		}
	}

	freehostent(hostinfo);

	(void) close(s);
	return (res);
}

#define	MAXIFS	32

/*
 * create an ifconf structure that represents all the interfaces
 * configured for this host.  Two buffers are allcated here:
 *	lifc - the ifconf structure returned
 *	lifc->lifc_buf - the list of ifreq structures
 * Both of the buffers must be freed by the calling routine.
 * A NULL pointer is returned upon failure.  In this case any
 * data that was allocated before the failure has already been
 * freed.
 */
struct lifconf *
getmyaddrs(void)
{
	int sock;
	struct lifnum lifn;
	int numifs;
	char *buf;
	struct lifconf *lifc;

	if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
		syslog(LOG_ERR, "statd:getmyaddrs socket: %m");
		return ((struct lifconf *)NULL);
	}

	lifn.lifn_family = AF_UNSPEC;
	lifn.lifn_flags = 0;

	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
		syslog(LOG_ERR,
		"statd:getmyaddrs, get number of interfaces, error: %m");
		numifs = MAXIFS;
	}

	numifs = lifn.lifn_count;

	lifc = (struct lifconf *)malloc(sizeof (struct lifconf));
	if (lifc == NULL) {
		syslog(LOG_ERR,
		    "statd:getmyaddrs, malloc for lifconf failed: %m");
		(void) close(sock);
		return ((struct lifconf *)NULL);
	}
	buf = (char *)malloc(numifs * sizeof (struct lifreq));
	if (buf == NULL) {
		syslog(LOG_ERR,
		    "statd:getmyaddrs, malloc for lifreq failed: %m");
		(void) close(sock);
		free(lifc);
		return ((struct lifconf *)NULL);
	}

	lifc->lifc_family = AF_UNSPEC;
	lifc->lifc_flags = 0;
	lifc->lifc_buf = buf;
	lifc->lifc_len = numifs * sizeof (struct lifreq);

	if (ioctl(sock, SIOCGLIFCONF, (char *)lifc) < 0) {
		syslog(LOG_ERR, "statd:getmyaddrs, SIOCGLIFCONF, error: %m");
		(void) close(sock);
		free(buf);
		free(lifc);
		return ((struct lifconf *)NULL);
	}

	(void) close(sock);

	return (lifc);
}

int
Is_ipv6present(void)
{
	int sock;
	struct lifnum lifn;

	sock = socket(AF_INET6, SOCK_DGRAM, 0);
	if (sock < 0)
		return (0);

	lifn.lifn_family = AF_INET6;
	lifn.lifn_flags = 0;
	if (ioctl(sock, SIOCGLIFNUM, (char *)&lifn) < 0) {
		close(sock);
		return (0);
	}
	close(sock);
	if (lifn.lifn_count == 0)
		return (0);
	return (1);
}