/*
 * Open a Port from Internet to a local process on FRITZ!Box
 *
 */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#include <netinet/in.h>

#include "extern.h"
#include "port_open.h"


typedef unsigned char u8;
typedef unsigned short u16;



/* ---------------------------------------------------------------- */
/* ------- /dev/kdsld_misc */
/* ---------------------------------------------------------------- */

#pragma pack(1)
struct kdslinterface_port_ipv4_open {
	unsigned      traffic_class;
	u16           port;
	u8            protocol;
	u8            autoclose;
};

struct kdslinterface_port_ipv4_close {
	unsigned      traffic_class;
	u16           port;
	u8            protocol;
};

struct kdslinterface_port_ipv6_open {
	unsigned      traffic_class;
	u16           port;
	u8            protocol;
	u8            autoclose;
};

struct kdslinterface_port_ipv6_close {
	unsigned      traffic_class;
	u16           port;
	u8            protocol;
};
#pragma pack()



#define KDSLD_PORT_IPV4_OPEN		_IOW('D',0x00, struct kdslinterface_port_ipv4_open)
#define KDSLD_PORT_IPV4_CLOSE		_IOW('D',0x01, struct kdslinterface_port_ipv4_close)
#define KDSLD_PORT_IPV6_OPEN		_IOW('D',0x02, struct kdslinterface_port_ipv6_open)
#define KDSLD_PORT_IPV6_CLOSE		_IOW('D',0x03, struct kdslinterface_port_ipv6_close)


static int dev_open(void)
{
	int fd = open("/dev/kdsld_misc", O_RDWR | O_NONBLOCK);
	if (fd >= 0) return fd;
	fd = open("/dev/kdsld_port", O_RDWR | O_NONBLOCK);
	return fd;
}

int OpenIPv4TCPPort(unsigned short port)
{
	int ret;
	int fd = dev_open();
	if (fd < 0) {
Log(("OpenIPv4TCPPort devopen failed"));
		return -EBADF;
	}

	struct kdslinterface_port_ipv4_open args;
	memset(&args, 0, sizeof(args));
	args.traffic_class = 0;
	args.protocol = IPPROTO_TCP;
	args.port = htons(port);
	args.autoclose = 1;
	ret = ioctl(fd, KDSLD_PORT_IPV4_OPEN, &args);
	if (ret != 0) ret = -errno;
	close(fd);
Log(("OpenIPv4TCPPort port=%u ret=%d", port, ret));
	return ret;
}

int CloseIPv4TCPPort(unsigned short port)
{
	int ret;
	int fd = dev_open();
	if (fd < 0) {
Log(("CloseIPv4TCPPort dev open failed"));
		return -EBADF;
	}

	struct kdslinterface_port_ipv4_close args;
	memset(&args, 0, sizeof(args));
	args.traffic_class = 0;
	args.protocol = IPPROTO_TCP;
	args.port = htons(port);
	ret = ioctl(fd, KDSLD_PORT_IPV4_CLOSE, &args);
	if (ret != 0) ret = -errno;
	close(fd);
Log(("CloseIPv4TCPPort port=%u ret=%d", port, ret));
	return ret;
}

#ifdef USE_IPV6
int OpenIPv6TCPPort(unsigned short port)
{
	int ret;
	int fd = dev_open();
	if (fd < 0) return -EBADF;

	struct kdslinterface_port_ipv6_open args;
	memset(&args, 0, sizeof(args));
	args.traffic_class = 0;
	args.protocol = IPPROTO_TCP;
	args.port = htons(port);
	args.autoclose = 1;
	ret = ioctl(fd, KDSLD_PORT_IPV6_OPEN, &args);
	if (ret != 0) ret = -errno;
	close(fd);
	return ret;
}

int CloseIPv6TCPPort(unsigned short port)
{
	int ret;
	int fd = dev_open();
	if (fd < 0) return -EBADF;

	struct kdslinterface_port_ipv6_close args;
	memset(&args, 0, sizeof(args));
	args.traffic_class = 0;
	args.protocol = IPPROTO_TCP;
	args.port = htons(port);
	ret = ioctl(fd, KDSLD_PORT_IPV6_CLOSE, &args);
	if (ret != 0) ret = -errno;
	close(fd);
	return ret;
}
#endif