// $Id: ifspec.c,v 1.4 2004/02/20 16:59:40 ensc Exp $

// Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
// based on ifspec.cc by Jacques Gelinas
//  
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2, or (at your option)
// any later version.
//  
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//  
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

/*
	Prints the specs of a network device in shell like form

	ADDR=
	NETMASK=
	BCAST=
*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>

static void usage()
{
	fprintf (stderr,"ifspec version %s\n",VERSION);
	fprintf (stderr
		,"ifspec network-device [ ipaddr netmask broadcast ]\n"
		 "prints device specification in a shell usable way\n");
	exit (-1);
}

static int ifconfig_ioctl(
	int fd,
	const char *ifname,
	int cmd,
	struct ifreq *ifr)
{
	strcpy(ifr->ifr_name, ifname);
	return ioctl(fd, cmd,ifr);
}

static unsigned long ip_cnv (const char *str)
{
	const char *start_str = str;
	unsigned tb[4];
	int no = 0;
	unsigned long ret;
	  
	memset (tb,-1,sizeof(tb));
	while (*str != '\0' && no < 4){
		if (isdigit(*str)){
			int val = atoi(str);
			if (val > 255) break;
			tb[no++] = val;
			while (isdigit(*str)) str++;
			if (*str == '.'){
				str++;
			}else{
				break;
			}
		}else{
			break;
		}
	}

	ret = (tb[0] << 24) | (tb[1]<<16) | (tb[2] << 8) | tb[3];
	if (no != 4 || *str != '\0'){
		fprintf (stderr,"Invalid IP number or netmask: %s\n",start_str);
		ret = 0xffffffff;
	}
	return ret;
}


/*
	Fetch the IP number of an interface from the kernel.
	Assume the device is already available in the kernel
	Return -1 if any error.
*/
int ifconfig_print (
	const char *ifname,
	const char *addrstr,
	const char *maskstr,
	const char *bcaststr)
{
	int ret = -1;
	int skfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (skfd != -1){
		struct ifreq ifr;
		struct {
			unsigned long addr;
			unsigned long mask;
		} solved = {0,0};
		if (addrstr != NULL && addrstr[0] != '\0'){
			printf ("ADDR=%s\n",addrstr);
			solved.addr = ip_cnv (addrstr);
		}else if (ifconfig_ioctl(skfd,ifname,SIOCGIFADDR, &ifr) >= 0){
			struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
			unsigned long addr = ntohl(sin->sin_addr.s_addr);
			printf ("ADDR=%lu.%lu.%lu.%lu\n"
				,(addr>>24)&0xff
				,(addr>>16)&0xff
				,(addr>>8)&0xff
				,addr&0xff);
			solved.addr = addr;
			ret = 0;
		}
		else {
		  perror("ifspec: ioctl(SIOCGIFADDR)");
		}
		
		if (maskstr != NULL && maskstr[0] != '\0'){
			printf ("NETMASK=%s\n",maskstr);
			solved.mask = ip_cnv (maskstr);
		}else		if (ifconfig_ioctl(skfd,ifname,SIOCGIFNETMASK, &ifr) >= 0){
			struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
			unsigned long addr = ntohl(sin->sin_addr.s_addr);
			printf ("NETMASK=%lu.%lu.%lu.%lu\n"
				,(addr>>24)&0xff
				,(addr>>16)&0xff
				,(addr>>8)&0xff
				,addr&0xff);
			solved.mask = addr;
			ret = 0;
		}
		else {
		  perror("ifspec: ioctl(SIOCGIFNETMASK)");
		}
		
		if (bcaststr != NULL && bcaststr[0] != '\0'){
			printf ("BCAST=%s\n",bcaststr);
		}else if (ifconfig_ioctl(skfd,ifname,SIOCGIFBRDADDR, &ifr) >= 0){
			struct sockaddr_in *sin = (struct sockaddr_in*)&ifr.ifr_addr;
			unsigned long addr = ntohl(sin->sin_addr.s_addr);
			printf ("BCAST=%lu.%lu.%lu.%lu\n"
				,(addr>>24)&0xff
				,(addr>>16)&0xff
				,(addr>>8)&0xff
				,addr&0xff);
			ret = 0;
		}else if (solved.addr!=0 && solved.mask!=0) {
			// Can't get it from the kernel, compute it from the IP
			// and the netmask
			unsigned long addr = (solved.addr & solved.mask)
				| ~solved.mask;
			printf ("BCAST=%lu.%lu.%lu.%lu\n"
				,(addr>>24)&0xff
				,(addr>>16)&0xff
				,(addr>>8)&0xff
				,addr&0xff);
			
		}
		close (skfd);
	}
	return ret;
}


int main (int argc, char *argv[])
{
	int ret = -1;
	if (argc < 2){
		usage();
	}else{
		const char *addrstr = argc >= 3 ? argv[2] : NULL;
		const char *maskstr = argc >= 4 ? argv[3] : NULL;
		const char *bcaststr = argc >= 5 ? argv[4] : NULL;
		ret = ifconfig_print (argv[1],addrstr,maskstr,bcaststr);
	}
	return ret;
}



