
/*  Copyright (c) 1995-1996 Caldera, Inc.  All Rights Reserved.

 *  See file COPYING for details.
 */

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <ncp/kernel/ipx.h>
#include <ncp/kernel/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#include "ipxutil.h"

#include "private/libintl.h"
#define _(X) gettext(X)

static struct ifreq id;
static char *progname;

static void
usage(void)
{
	fprintf(stderr,
		_("Usage: %s add [-p] device frame_type [net_number[:node]]\n"
		  "Usage: %s del device frame_type\n"
                  "Usage: %s delall\n"
                  "Usage: %s check device frame_type\n"),
		progname, progname, progname, progname);
}

struct frame_type
{
	const char *ft_name;
	unsigned char ft_val;
}
frame_types[] =
{
	{
		"802.2", IPX_FRAME_8022
	}
	,
#ifdef IPX_FRAME_TR_8022
	{
		"802.2TR", IPX_FRAME_TR_8022
	}
	,
#endif
	{
		"802.3", IPX_FRAME_8023
	}
	,
	{
		"SNAP", IPX_FRAME_SNAP
	}
	,
	{
		"EtherII", IPX_FRAME_ETHERII
	}
};

#define NFTYPES	(sizeof(frame_types)/sizeof(struct frame_type))

static int
lookup_frame_type(char *frame)
{
	size_t j;

	for (j = 0; (j < NFTYPES) &&
	     (strcasecmp(frame_types[j].ft_name, frame));
	     j++)
		;

	if (j != NFTYPES)
		return j;

	fprintf(stderr, _("%s: Frame type must be"), progname);
	for (j = 0; j < NFTYPES; j++)
	{
		fprintf(stderr, "%s%s",
			(j == NFTYPES - 1) ? _(" or ") : " ",
			frame_types[j].ft_name);
	}
	fprintf(stderr, ".\n");
	return -1;
}

static int
ipx_add_interface(int argc, char **argv)
{
	struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &id.ifr_addr;
	int s;
	int result;
	int i, fti = 0;
	int c;

	sipx->sipx_special = IPX_SPECIAL_NONE;
	sipx->sipx_network = 0L;
	memset(sipx->sipx_node, 0, sizeof(sipx->sipx_node));
	sipx->sipx_type = IPX_FRAME_NONE;
	while ((c = getopt(argc, argv, "p")) > 0)
	{
		switch (c)
		{
		case 'p':
			sipx->sipx_special = IPX_PRIMARY;
			break;
		}
	}

	if (((i = (argc - optind)) < 2) || (i > 3))
	{
		usage();
		return -1;
	}
	for (i = optind; i < argc; i++)
	{
		switch (i - optind)
		{
		case 0:	/* Physical Device - Required */
			strcpy(id.ifr_name, argv[i]);
			break;
		case 1:	/* Frame Type - Required */
			fti = lookup_frame_type(argv[i]);
			if (fti < 0) {
				return -1;
			}
			sipx->sipx_type = frame_types[fti].ft_val;
			break;

		case 2:	/* Network Number - Optional */
			sscanf_ipx_addr(sipx, argv[i], SSIPX_NETWORK | SSIPX_NODE);
			if (sipx->sipx_network == htonl(0xffffffffL))
			{
				fprintf(stderr,
				   _("%s: Inappropriate network number %08lX\n"),
					progname, (unsigned long)ntohl(sipx->sipx_network));
				return -1;
			}
			break;
		}
	}

	s = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
	if (s < 0)
	{
		int old_errno = errno;
		fprintf(stderr, _("%s: socket: %s\n"), progname,
			strerror(errno));
		if (old_errno == -EINVAL)
		{
			fprintf(stderr, _("Probably you have no IPX support in "
				"your kernel\n"));
		}
		return -1;
	}
	i = 0;
	sipx->sipx_family = AF_IPX;
	sipx->sipx_action = IPX_CRTITF;
	do
	{
		result = ioctl(s, SIOCSIFADDR, &id);
		i++;
	}
	while ((i < 5) && (result < 0) && (errno == EAGAIN));

	if (result == 0) {
		return 0;
	}

	switch (errno)
	{
	case EEXIST:
		fprintf(stderr, _("%s: Primary network already selected.\n"),
			progname);
		break;
	case EADDRINUSE:
		fprintf(stderr, _("%s: Network number (%08X) already in use.\n"),
			progname, (u_int32_t)htonl(sipx->sipx_network));
		break;
	case EPROTONOSUPPORT:
		fprintf(stderr, _("%s: Invalid frame type (%s).\n"),
			progname, frame_types[fti].ft_name);
		break;
	case ENODEV:
		fprintf(stderr, _("%s: No such device (%s).\n"), progname,
			id.ifr_name);
		break;
	case ENETDOWN:
		fprintf(stderr, _("%s: Requested device (%s) is down.\n"), progname,
			id.ifr_name);
		break;
	case EINVAL:
		fprintf(stderr, _("%s: Invalid device (%s).\n"), progname,
			id.ifr_name);
		break;
	case EAGAIN:
		fprintf(stderr,
			_("%s: Insufficient memory to create interface.\n"),
			progname);
		break;
	default:
		fprintf(stderr, _("%s: ioctl: %s\n"), progname, 
			strerror(errno));
		break;
	}
	close(s);
	return -1;
}

static int
ipx_delall_interface(int argc, char **argv)
{
	struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &id.ifr_addr;
	int s;
	int result;
	char buffer[80];
	char device[20];
	char frame_type[20];
	int fti;
	FILE *fp;

	s = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
	if (s < 0)
	{
		fprintf(stderr, _("%s: socket: %s\n"), progname,
			strerror(errno));
		return -1;
	}
	fp = fopen("/proc/net/ipx_interface", "r");
	if (!fp) {
		fp = fopen("/proc/net/ipx/interface", "r");
	}
	if (fp == NULL)
	{
		close(s);
		fprintf(stderr,
			_("%s: Unable to open \"%s.\"\n"),
			progname,
			"/proc/net/ipx_interface");
		return -1;
	}
	fgets(buffer, 80, fp);
	while (fscanf(fp, "%s %s %s %s %s", buffer, buffer, buffer,
		      device, frame_type) == 5)
	{

		sipx->sipx_network = 0L;
		if (strcasecmp(device, "Internal") == 0)
		{
			sipx->sipx_special = IPX_INTERNAL;
		} else
		{
			sipx->sipx_special = IPX_SPECIAL_NONE;
			strcpy(id.ifr_name, device);
			fti = lookup_frame_type(frame_type);
			if (fti < 0)
				continue;
			sipx->sipx_type = frame_types[fti].ft_val;
		}

		sipx->sipx_action = IPX_DLTITF;
		sipx->sipx_family = AF_IPX;
		result = ioctl(s, SIOCSIFADDR, &id);
		if (result == 0)
			continue;
		switch (errno)
		{
		case EPROTONOSUPPORT:
			fprintf(stderr, _("%s: Invalid frame type (%s).\n"),
				progname, frame_type);
			break;
		case ENODEV:
			fprintf(stderr, _("%s: No such device (%s).\n"),
				progname, device);
			break;
		case EINVAL:
			fprintf(stderr, _("%s: No such IPX interface %s %s.\n"),
				progname, device, frame_type);
			break;
		default:
			fprintf(stderr, _("%s: ioctl: %s\n"), progname,
				strerror(errno));
			break;
		}
	}
	fclose(fp);
	close(s);
	return 0;
}

static int
ipx_del_interface(int argc, char **argv)
{
	struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &id.ifr_addr;
	int s;
	int result;
	int fti;

	if (argc != 2)
	{
		usage();
		return -1;
	}
	sipx->sipx_network = 0L;
	sipx->sipx_special = IPX_SPECIAL_NONE;
	strcpy(id.ifr_name, argv[0]);
	fti = lookup_frame_type(argv[1]);
	if (fti < 0) {
		return -1;
	}
	sipx->sipx_type = frame_types[fti].ft_val;

	s = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
	if (s < 0)
	{
		fprintf(stderr, _("%s: socket: %s\n"), progname,
			strerror(errno));
		return -1;
	}
	sipx->sipx_action = IPX_DLTITF;
	sipx->sipx_family = AF_IPX;
	result = ioctl(s, SIOCSIFADDR, &id);
	if (result == 0) {
		close(s);
		return 0;
	}

	switch (errno)
	{
	case EPROTONOSUPPORT:
		fprintf(stderr, _("%s: Invalid frame type (%s).\n"),
			progname, frame_types[fti].ft_name);
		break;
	case ENODEV:
		fprintf(stderr, _("%s: No such device (%s).\n"), progname,
			id.ifr_name);
		break;
	case EINVAL:
		fprintf(stderr, _("%s: No such IPX interface %s %s.\n"), progname,
			id.ifr_name, frame_types[fti].ft_name);
		break;
	default:
		fprintf(stderr, _("%s: ioctl: %s\n"), progname,
			strerror(errno));
		break;
	}
	close(s);
	return -1;
}

static int
ipx_check_interface(int argc, char **argv)
{
	struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &id.ifr_addr;
	int s;
	int result;
	int fti;

	if (argc != 2)
	{
		usage();
		return -1;
	}
	sipx->sipx_network = 0L;
	strcpy(id.ifr_name, argv[0]);
	fti = lookup_frame_type(argv[1]);
	if (fti < 0) {
		return -1;
	}
	sipx->sipx_type = frame_types[fti].ft_val;

	s = socket(AF_IPX, SOCK_DGRAM, AF_IPX);
	if (s < 0)
	{
		fprintf(stderr, _("%s: socket: %s\n"), progname,
			strerror(errno));
		return -1;
	}
	sipx->sipx_family = AF_IPX;
	result = ioctl(s, SIOCGIFADDR, &id);
	if (result == 0)
	{
		close(s);
		printf(
			      _("IPX Address for (%s, %s) is %08X:%02X%02X%02X%02X%02X%02X.\n"),
			      argv[1], frame_types[fti].ft_name,
			      (u_int32_t)htonl(sipx->sipx_network), sipx->sipx_node[0],
			      sipx->sipx_node[1], sipx->sipx_node[2],
			      sipx->sipx_node[3], sipx->sipx_node[4],
			      sipx->sipx_node[5]);
		return 0;
	}
	switch (errno)
	{
	case EPROTONOSUPPORT:
		fprintf(stderr, _("%s: Invalid frame type (%s).\n"),
			progname, frame_types[fti].ft_name);
		break;
	case ENODEV:
		fprintf(stderr, _("%s: No such device (%s).\n"), progname,
			id.ifr_name);
		break;
	case EADDRNOTAVAIL:
		fprintf(stderr, _("%s: No such IPX interface %s %s.\n"), progname,
			id.ifr_name, frame_types[fti].ft_name);
		break;
	default:
		fprintf(stderr, _("%s: ioctl: %s\n"), progname, 
			strerror(errno));
		break;
	}
	close(s);
	return -1;
}

int
main(int argc, char **argv)
{
	int i;

	progname = argv[0];

	setlocale(LC_ALL, "");
	bindtextdomain(NCPFS_PACKAGE, LOCALEDIR);
	textdomain(NCPFS_PACKAGE);

	if (argc < 2)
	{
		usage();
		return -1;
	}
	if (strcasecmp(argv[1], "add") == 0)
	{
		for (i = 1; i < (argc - 1); i++)
			argv[i] = argv[i + 1];
		return ipx_add_interface(argc - 1, argv);
	} else if (strcasecmp(argv[1], "delall") == 0)
	{
		return ipx_delall_interface(argc - 2, argv + 2);
	} else if (strcasecmp(argv[1], "del") == 0)
	{
		return ipx_del_interface(argc - 2, argv + 2);
	} else if (strcasecmp(argv[1], "check") == 0)
	{
		return ipx_check_interface(argc - 2, argv + 2);
	}
	usage();
	return -1;
}
