/* Copyright 2004 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
 *
 * 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 of the License, 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
 */


#include <stdio.h>			/* *printf */
#include <string.h>			/* mem* */

#include "ipset.h"

#include <linux/netfilter_ipv4/ip_set_protocol.h>

static const char* protocol_type[] = {"DNS","FTP","HTTP","IMAP","IRC","NNTP","POP3","SMB","SMTP","SNMP","SSH","SSL/TLS","TELNET", "MSSQL", "MYSQL", "ORACLE", "POSTGRESQL", "SYBASE", "DB2", "INFORMIX"};

/* Initialize the create. */
static void
protocol_create_init(void *data UNUSED)
{
	struct ip_set_req_protocol_create *mydata = data;
	
	DP("create INIT");

	mydata->from = PROTOCOL_FROM;
	mydata->to = PROTOCOL_TO;
	
	/* Nothing */
}

/* Function which parses command options; returns true if it ate an option */
static int
protocol_create_parse(int c, char *argv[] UNUSED, void *data, unsigned *flags)
{
	DP("create_parse");	
	return 1;
}

/* Final check; exit if not ok. */
static void
protocol_create_final(void *data, unsigned int flags)
{
	return;
}

/* Create commandline options */
static const struct option create_opts[] = {
	{NULL},
};

/* Add, del, test parser */
static ip_set_ip_t
protocol_adt_parser(int cmd UNUSED, const char *arg, void *data)
{
	struct ip_set_req_protocol *mydata = data;

	DP("%s", arg);

	if (strings_to_index(protocol_type, PROTOCOL_NUM, arg, &mydata->ip) != 0)
	{
		exit_error(PARAMETER_PROBLEM, 
		           "Invalid protcol `%s' specified", arg);
		return 0;
	}
	return 1;	
}

/*
 * Print and save
 */

static void
protocol_initheader(struct set *set, const void *data)
{
	const struct ip_set_req_protocol_create *header = data;
	struct ip_set_protocol *map = set->settype->header;

	memset(map, 0, sizeof(struct ip_set_protocol));
	map->first_ip = header->from;
	map->last_ip = header->to;
}

static void
protocol_printheader(struct set *set, unsigned options)
{

	printf("%s %s\n", 
	       set->name,
	       set->settype->typename);

}

static inline void
__protocol_printips_sorted(struct set *set, void *data,
			  u_int32_t len UNUSED, unsigned options)
{
	struct ip_set_protocol *mysetdata = set->settype->header;
	ip_set_ip_t addr = mysetdata->first_ip;

	DP("%u -- %u", mysetdata->first_ip, mysetdata->last_ip);
	while (addr <= mysetdata->last_ip) {
		if (test_bit(addr - mysetdata->first_ip, data))
			printf("%s\n", protocol_type[addr]);
		addr++;
	}
}

static void
protocol_printips_sorted(struct set *set, void *data,
			u_int32_t len, unsigned options,
			char dont_align)
{
	ip_set_ip_t *ip;
	size_t offset = 0;
	
	if (dont_align)
		return __protocol_printips_sorted(set, data, len, options);
	
	while (offset < len) {
		ip = data + offset;
		printf("%s\n", protocol_type[*ip]);
		offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
	}
}

static void
protocol_saveheader(struct set *set, unsigned options)
{

	printf("-N %s %s\n", 
	       set->name,
	       set->settype->typename);
}

static inline void
__protocol_saveips(struct set *set, void *data,
		  u_int32_t len UNUSED, unsigned options)
{
	struct ip_set_protocol *mysetdata = set->settype->header;
	ip_set_ip_t addr = mysetdata->first_ip;

	while (addr <= mysetdata->last_ip) {
		DP("addr: %lu, last_ip %lu", (long unsigned)addr, (long unsigned)mysetdata->last_ip);
		if (test_bit(addr - mysetdata->first_ip, data))
			printf("-A %s %s\n",
			       set->name,
			       protocol_type[addr]);
		addr++;
	}
}

static void
protocol_saveips(struct set *set, void *data,
		u_int32_t len, unsigned options,
		char dont_align)
{
	ip_set_ip_t *ip;
	size_t offset = 0;
	
	if (dont_align)
		return __protocol_saveips(set, data, len, options);
	
	while (offset < len) {
		ip = data + offset;
		printf("-A %s %s\n", set->name, port_tostring(*ip, options));
		offset += IPSET_ALIGN(sizeof(ip_set_ip_t));
	}
}

static void
protocol_usage(void)
{
	printf
	    ("-N set protocol\n");
	strings_print(protocol_type, PROTOCOL_NUM, "-ADT %s\n");
}

static struct settype settype_protocol = {
	.typename = SETTYPE_NAME,
	.protocol_version = IP_SET_PROTOCOL_VERSION,

	/* Create */
	.create_size = sizeof(struct ip_set_req_protocol_create),
	.create_init = protocol_create_init,
	.create_parse = protocol_create_parse,
	.create_final = protocol_create_final,
	.create_opts = create_opts,

	/* Add/del/test */
	.adt_size = sizeof(struct ip_set_req_protocol),
	.adt_parser = protocol_adt_parser,

	/* Printing */
	.header_size = sizeof(struct ip_set_protocol),
	.initheader = protocol_initheader,
	.printheader = protocol_printheader,
	.printips = protocol_printips_sorted,
	.printips_sorted = protocol_printips_sorted,
	.saveheader = protocol_saveheader,
	.saveips = protocol_saveips,
	
	.usage = protocol_usage,
};

CONSTRUCTOR(protocol)
{
	settype_register(&settype_protocol);

}
