%{
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <stdio.h>
#include <string.h>
#ifdef HAVE_STRING_H
#include <strings.h>
#endif

#include "trace.h"

#ifdef RPSL_IP_DEBUG
struct rpsl_ip { 
	struct in_addr addr;
	unsigned int len,len1,len2;
} rpsl_ip;
int debug=0;
#else
#include "ro.h"
extern int debug;
struct rpsl_ip rpsl_ip;
#endif

static int explen;

#define LONG2ADDR(addr) (*(struct in_addr*)&addr)
#define FILTER_PREFIX 1
#define FILTER_EACL 2
#define FILTER_GATED 3

%}

INT			[[:digit:]]+
IPV4		[0-9]+(\.[0-9]+){3,3}
PRFXV4		{IPV4}\/[0-9]+
PRFXV4RNG	{PRFXV4}("^+"|"^-"|"^"{INT}|"^"{INT}-{INT})

%%
{IPV4}	{ 
		if(debug>1)
		SX_TRACE2("Found ip address %s\n",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		fprintf(stderr,"!no masklen given, using /24 (%s)",yytext);
		rpsl_ip.len=24;
		return 1;
		};
{PRFXV4}	{ 
		char* c;
		if(debug>1)
		SX_TRACE2("Found ip prefix %s",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		c=index(yytext,'/');
		*c++=0;
		rpsl_ip.len=atoi(c);
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		return 1;
		};
{PRFXV4}"^+"	{ /* all more specific routes */
		char* c;
		if(debug>1)
		SX_TRACE2("Found 'all more specifics including' of %s",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		c=index(yytext,'/');
		*c++=0;
		rpsl_ip.len=atoi(c);;
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		rpsl_ip.len1=rpsl_ip.len;
		rpsl_ip.len2=32;
		return 1;
		};
{PRFXV4}"^-" 	{ /* all more specific excluding */
		char* c;
		if(debug>1)
		SX_TRACE2("Found 'all more specifics excluding' of %s",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		c=index(yytext,'/');
		*c++=0;
		rpsl_ip.len=atoi(c);;
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		rpsl_ip.len1=rpsl_ip.len+1;
		rpsl_ip.len2=32;
		return 1;
		};
{PRFXV4}"^"{INT}	{ /* all specifics of specified length */
		char* c;
		if(debug>1)
		SX_TRACE2("Found 'length-specified specifics' of %s",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		c=index(yytext,'/');
		*c++=0;
		rpsl_ip.len=atoi(c);;
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		c=index(c,'^')+1;
		rpsl_ip.len1=rpsl_ip.len2=atoi(c);
		return 1;
		};
{PRFXV4}"^"{INT}"-"{INT}	{ /* all specifics in range */
		char* c;
		if(debug>1)
		SX_TRACE2("Found 'all specifics in range' of %s",yytext);
		memset(&rpsl_ip,0,sizeof(rpsl_ip));
		explen=strlen(yytext);
		c=index(yytext,'/');
		*c++=0;
		rpsl_ip.len=atoi(c);;
		if(!inet_aton(yytext,&rpsl_ip.addr)) { 
			return 0;
		};
		c=index(c,'^')+1;
		rpsl_ip.len1=atoi(c);
		c=index(c,'-')+1;
		rpsl_ip.len2=atoi(c);
		return 1;
		};
.	{ SX_TRACE2("unknown char..."); return 1; } 
%%

int
rpsl_print_filter(FILE* whence, struct rpsl_ip* rpsl, int type)
{ 
	if(type==FILTER_PREFIX) { 
		fprintf(whence,"%s/%i",inet_ntoa(rpsl->addr),rpsl->len);
		if(rpsl->len1) fprintf(whence," ge %i",rpsl->len1);
		if(rpsl->len2 && rpsl->len2!=32) fprintf(whence," le %i",rpsl->len2);
		return 1;
	};

	if(type==FILTER_GATED) { 
		fprintf(whence,"%s masklen %i",inet_ntoa(rpsl->addr),rpsl->len); 
		if(rpsl->len1 && rpsl->len2) {
			if(rpsl->len1==rpsl->len2 && rpsl->len1==rpsl->len)
				fprintf(whence," exact");
			else
				fprintf(whence," between %i and %i",rpsl->len1,rpsl->len2); 
		};
		fprintf(whence,";");
		return 1;
	};

	if(type==FILTER_EACL) { 
		unsigned long mask=0xffffffffl, wildaddr, wild2addr,wildmask;
		int masklen=rpsl->len1?rpsl->len1:rpsl->len;

		wildaddr=0xffffffffl>>rpsl->len;
		if(rpsl->len2==32) wild2addr=0;
		else wild2addr=0xffffffffl>>rpsl->len2;

		wildaddr=wildaddr&(~wild2addr);

		if(masklen==32) mask=0xffffffffl;
		else mask=0xffffffffl<<(32-masklen);

		wildmask=(0xffffffffl>>rpsl->len1)&(~wild2addr);

		mask=htonl(mask);
		wildaddr=htonl(wildaddr);
		wildmask=htonl(wildmask);

		if(rpsl->len==32) wild2addr=0xffffffffl;
		else wild2addr=0xffffffffl<<(32-rpsl->len);

		rpsl->addr.s_addr=rpsl->addr.s_addr&htonl(wild2addr);

		if(wildaddr) { 
			fprintf(whence,"%s ",inet_ntoa(rpsl->addr));
			fprintf(whence,"%s ",inet_ntoa(LONG2ADDR(wildaddr)));
		} else 
			fprintf(whence,"host %s ",inet_ntoa(rpsl->addr));

		if(wildmask) { 
			fprintf(whence,"%s ",inet_ntoa(LONG2ADDR(mask)));
			fprintf(whence,"%s ",inet_ntoa(LONG2ADDR(wildmask)));
		} else {
			fprintf(whence,"host %s ",inet_ntoa(LONG2ADDR(mask)));
		};
		return 1;
	};
	return 0;
};
int 
yywrap()
{ 
	return 1;
};

struct rpsl_ip* 
rpsl_translate(char* addr)
{ 
	YY_BUFFER_STATE ybufer;
	struct rpsl_ip* rp;

	ybufer=yy_scan_string(addr);
	yylex();
	yy_delete_buffer(ybufer);
	if(explen!=strlen(addr)) { 
		SX_TRACE(1,"Wrong encoded %s - matched just %i bytes from %i total",
			addr,explen,strlen(addr));
		return NULL;
	};
	if(rpsl_ip.len>32 || rpsl_ip.len<0 ||
		rpsl_ip.len1>32 || rpsl_ip.len1<0 ||
		rpsl_ip.len2>32 || rpsl_ip.len2<0 ||
		(rpsl_ip.len1 && rpsl_ip.len>rpsl_ip.len1) || 
		(rpsl_ip.len2 && rpsl_ip.len1>rpsl_ip.len2))
			return NULL;
	rp=malloc(sizeof(struct rpsl_ip));
	if(!rp) return NULL;
	*rp=rpsl_ip; /* structure copy */
	return rp;
};

int
rpsl_filters(struct rpsl_ip* rpsl, unsigned long addr, int masklen)
{ 
	unsigned long wildaddr, wild2addr;

	wildaddr=0xffffffffl>>rpsl->len;
	if(rpsl->len2==32) wild2addr=0;
	else wild2addr=0xffffffffl>>rpsl->len2;

	wildaddr=htonl(wildaddr&(~wild2addr));

	if((rpsl->addr.s_addr^addr)&(~wildaddr)) return 0;

	if(!rpsl->len1 && masklen!=rpsl->len) return 0;
	if(!rpsl->len2 && masklen!=rpsl->len1) return 0;
	
	if(masklen<rpsl->len1 || masklen>rpsl->len2) return 0;

	return 1;
};

int
rpsl_free(struct rpsl_ip* rpsl)
{ 
	if(rpsl) free(rpsl);
	return 1;
};

	
#ifdef RPSL_IP_DEBUG
int
main()
{ 
	char buffer[2048];
	struct rpsl_ip* rp;

	while(fgets(buffer,sizeof(buffer),stdin)) { 
		rp=rpsl_translate(buffer);
		if(!rp) { 
			SX_TRACE2("non-translatable string %s",buffer);
			continue;
		};
		rpsl_print_filter(stdout,rp,FILTER_PREFIX);
		fprintf(stdout,"\n");
		rpsl_print_filter(stdout,rp,FILTER_EACL);
		fprintf(stdout,"\n");
	};
	return 0;
};
#endif
