/*

  ip - a little snipplet to fetch IP address..

  (C) Jon Langseth, 2007
  All rights and lefts reserved

  Compile with:
    gcc ip.c -o ip
  
  Run with
    ./ip -m46ieth0 for example

   -m Show MAC afdresses
   -4 Show IPv4 addresses
   -6 show IPv6 addresses
   -i <int> Show only this interface

*/

#include <errno.h>            
#include <sys/ioctl.h>        
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <ifaddrs.h>
#include <string.h>
#include <unistd.h>

extern int errno;

char * getIfName ( struct ifaddrs * ifa ) 
{
	char* name;
	if ( ifa->ifa_name ) 
	{
		name = malloc(strlen(ifa->ifa_name) * sizeof(char));
		sprintf(name, "%s", ifa->ifa_name);
		return name;
	}
	return NULL;
}

char * getIp4Address ( struct ifaddrs * ifa ) 
{
	socklen_t salen;

	if (ifa->ifa_addr->sa_family == AF_INET)
	{
		salen = sizeof (struct sockaddr_in);
	} else {
		return NULL;
	}
	char * ip = malloc(salen * sizeof(char) + 10);

	if (getnameinfo (ifa->ifa_addr, salen, ip, (salen * sizeof(char) + 10), NULL, 0, NI_NUMERICHOST) < 0)
	{
		perror ("Failed getnameinfo, unable to get address");
		return NULL;
	}

	return ip;
}

char * getIp6Address ( struct ifaddrs * ifa ) 
{
	socklen_t salen;

	if (ifa->ifa_addr->sa_family == AF_INET6)
	{
		salen = sizeof (struct sockaddr_in6);
	} else {
		return NULL;
	}
	char * ip = malloc(salen * sizeof(char) + 10);

	if ( getnameinfo (ifa->ifa_addr, salen, ip, (salen * sizeof(char) + 10), NULL, 0, NI_NUMERICHOST) )
	{
		perror ("Failed getnameinfo, unable to get address");
		printf(strerror(errno));
	}

	return ip;
}
char * getMacAddress ( struct ifaddrs * ifa ) 
{
	char * mac;
	struct ifreq devn;
	strcpy(devn.ifr_name, getIfName(ifa));
	int s = socket(AF_INET, SOCK_DGRAM, 0);
	if ( s < 0 ) 
	{
		perror("Failed socket");
		return NULL;
	}
	if (ioctl(s,SIOCGIFHWADDR,&devn) < 0) 
	{
		perror("Failed ioctl");
		return NULL;
	}
	mac = malloc(20*sizeof(char));
	sprintf(mac, "%.2X:%.2X:%.2X:%.2X:%.2X:%.2X", 
		devn.ifr_ifru.ifru_hwaddr.sa_data[0]&0xff,
		devn.ifr_ifru.ifru_hwaddr.sa_data[1]&0xff,
		devn.ifr_ifru.ifru_hwaddr.sa_data[2]&0xff,
		devn.ifr_ifru.ifru_hwaddr.sa_data[3]&0xff, 
		devn.ifr_ifru.ifru_hwaddr.sa_data[4]&0xff,
		devn.ifr_ifru.ifru_hwaddr.sa_data[5]&0xff); 
	return mac;
}

int main (int argc, char *argv[])
{

	int v4 = 0;
	int v6 = 0;
	int m = 0;
	int dev = 0;
	int c;
	char * devname = NULL;

	struct ifaddrs *ifa = NULL, *ifp = NULL;

	while ((c = getopt (argc, argv, "46mi:")) != -1)
	{
		switch (c)
		{
			case '4':
				v4 = 1;
				break;
			case '6':
				v6 = 1;
				break;
			case 'm':
				m = 1;
				break;
			case 'i':
				dev = 1;
				devname = optarg;
				break;
			case '?':
				if (isprint (optopt))
					fprintf (stderr, "Unknown option `-%c'.\n", optopt);
				else
					fprintf (stderr,
							"Unknown option character `\\x%x'.\n",
							optopt);
				return 1;
			default:
				abort ();
		}
	}


	if (getifaddrs (&ifp) < 0)
	{
		perror ("Failed getifaddrs, unable to get interfaces");
		return 1;
	}

	for (ifa = ifp; ifa; ifa = ifa->ifa_next)
	{
		char ip[ 200 ];
		socklen_t salen;
		int type = 0;

		char * device;
		char *temp;
		device = getIfName(ifa);

		if ( dev && ( strcmp(getIfName(ifa),devname) ) )
			continue;

		if ( ( m ) && ( ifa->ifa_addr->sa_family == AF_PACKET) ) 
		{
			temp = getMacAddress(ifa);
			if ( device && temp )
				printf("%s MAC %s\n" , device, temp); 
			free(temp);
		}
		else 
		if ( ( v4 ) && ( ifa->ifa_addr->sa_family == AF_INET) ) 
		{
			temp = getIp4Address(ifa);
			if ( device && temp )
				printf("%s IP4 %s\n" , device, temp); 
			free(temp);
		}
		else 
		if ( ( v6 ) && ( ifa->ifa_addr->sa_family == AF_INET6) ) 
		{
			temp = getIp6Address(ifa);
			if ( device && temp )
				printf("%s IP6 %s\n" , device, temp); 
			free(temp);
		}
	}

	freeifaddrs (ifp);
	return 0;
}
