// Copyright (C) FIT VUT
// Petr Lampa <lampa@fit.vutbr.cz>
// $Id$
// vi:set ts=8 sts=8 sw=8:
//
//

#ifndef __NDWATCH_UTILS_H__

extern "C" {
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <sys/socket.h>
#ifdef __linux__
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#else
#include <net/if_dl.h>
#endif
}

bool dns_lookup6(const string &name, IPv6Address &ipv6)
{
	struct addrinfo hints, *result, *res;
	int error;

	memset(&hints, 0, sizeof(addrinfo));
	hints.ai_family = PF_INET6;
	if ((error = getaddrinfo(name.c_str(), NULL, &hints, &result)) != 0) {
		if (debug) cerr << "error " << gai_strerror(error) << " lookup " << name << endl;
		return false;
	}
	for (res = result; res != NULL; res = res->ai_next) {
		if (res->ai_family == AF_INET6) {
			ipv6.addr(res->ai_addr);
			freeaddrinfo(result);
			return true;
		}
	}
	freeaddrinfo(result);
	return false;
}

bool if_get_mac(const string &device, MacAddress &mac)
{
	struct ifaddrs *ifa = NULL, *ife;
	if (getifaddrs(&ifa)) return false;
	bool ret = false;
	for (ife = ifa; ife != NULL; ife = ife->ifa_next) {
		if (ife->ifa_addr == NULL) continue;
		if (strcmp(ife->ifa_name, device.c_str()) != 0) continue;
		if (ife->ifa_addr->sa_family == AF_INET) {
		} else
		if (ife->ifa_addr->sa_family == AF_INET6) {
		} else
#ifdef __linux__
		if (ife->ifa_addr->sa_family == AF_PACKET) {
                        int fd = socket(AF_INET, SOCK_DGRAM, 0);
                        struct ifreq ifr;
                        ifr.ifr_addr.sa_family = AF_INET;
                        strcpy(ifr.ifr_name, ife->ifa_name);
                        ioctl(fd, SIOCGIFHWADDR, &ifr);
                        close(fd);
                        mac.addr(reinterpret_cast<Octet *>(ifr.ifr_hwaddr.sa_data));
                        ret = true;
                        break;
#else
                // Posix, FreeBSD, Mac OS X
		if (ife->ifa_addr->sa_family == AF_LINK &&
                    reinterpret_cast<const struct sockaddr_dl *>(ife->ifa_addr)->sdl_alen == 6) {
			mac.addr(reinterpret_cast<Octet *>(LLADDR(reinterpret_cast<const struct sockaddr_dl *>(ife->ifa_addr))));
			ret = true;
			break;
#endif
		} else {
			// cerr << "family " << (int)ife->ifa_addr->sa_family << endl;
		}
	}	
	freeifaddrs(ifa);
	return ret;
}

bool if_get_ipv6(const string &device, IPv6Address &ipv6, int &plen, int i)
{
	struct ifaddrs *ifa = NULL, *ife;
	plen = -1;
	if (getifaddrs(&ifa)) return false;
	bool ret = false;
	for (ife = ifa; ife != NULL; ife = ife->ifa_next) {
		if (ife->ifa_addr == NULL) continue;
		if (strcmp(ife->ifa_name, device.c_str()) != 0) continue;
		if (ife->ifa_addr->sa_family == AF_INET6 && i-- <= 0) {
			ipv6.addr(ife->ifa_addr);
			if (ife->ifa_netmask && ife->ifa_netmask->sa_family == AF_INET6) {
				Octet *p = reinterpret_cast<struct sockaddr_in6 *>(ife->ifa_netmask)->sin6_addr.s6_addr;
				plen = 0;
				for (int n = 0; n < 16; n++) {
					if (*p == 0xff) {
						plen += 8;
						p++;
						continue;
					}
					Octet m = 0x80;
					for (int b = 0; b < 8; b++) {
						if ((*p & m) == 0) break;
						plen++;
						m = m >> 1;
					}
					break;
				}
			}
			ret = true;
			break;
		}
	}	
	freeifaddrs(ifa);
	return ret;
}
#endif
