//
// 
//	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
// 
//	This program is free software; you can redistribute it 
//	and/or modify it under the terms of the GLOBALBASE 
//	Library General Public License (G-LGPL) as published by 
//
//	http://www.globalbase.org/
// 
//	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.
//
//

extern "C" {

#include	<LInternetAddress.h>
#include	"netutils.h"
#include	"task.h"
#include	"blacklist.h"
#include	"memory_debug.h"
int get_xltime();
int bl_do(void * cmd);
extern SEM netutils_lock;
#define AF_INET 2
int gethostbyaddr_rr_retry(BLACKLIST * bl);
void er_panic(char*);
struct hostent * get_new_hostent();
struct hostent * search_hostent_hash(int len, char *address);
void add_to_hostent_hash(struct hostent *he);

void _ExStop(void);
void _ExRevert(void);


typedef struct gethostbyaddr_cmd {
	BL_CMD			cmd;
	struct hostent *	h;
} GETHOSTBYADDR_CMD;

struct hostent * _gethostbyaddr_rr(char * addr,int len,int type);
int gethostbyaddr_rr_proc(BLACKLIST * bl,GETHOSTBYADDR_CMD * cmd);

struct hostent *
_gethostbyaddr_rr(char * addr,int len,int type)
{
	if (len != 4)
		return 0;

	struct hostent * ret;
	ret = search_hostent_hash(len,addr);
	if ( ret )
		goto end;
	
	ret = get_new_hostent();
	_ExStop();
	try
	{
		LInternetAddress ia;
		ia.SetIPAddress(*(int *)addr);
		*(int*)ret->h_addr = *(int *)addr;
		ia.GetDNSAddress((unsigned char *)ret->h_name);
		p2cstrcpy(ret->h_name, (unsigned char *)ret->h_name);
	}
	catch(...)
	{
		ret = 0;
	}
	_ExRevert();

	if ( ret )
		add_to_hostent_hash(ret);
end:
	return ret;
}

int
gethostbyaddr_rr_proc(BLACKLIST * bl,GETHOSTBYADDR_CMD * cmd)
{
int type;
int * ptr;
char * addr;
int addr_len;
unsigned int t;

	t = get_xltime();

	addr_len = cmd->cmd.name_len - sizeof(type);
	ptr = (int*)cmd->cmd.name;
	type = *ptr;
	ptr ++;
	addr = (char*)ptr;
	cmd->h = _gethostbyaddr_rr(addr,addr_len,type);

	bl->polling_initial = 2*(get_xltime() - t);

	if ( cmd->h )
		return BLS_OK;
	return BLS_ERROR;
}

struct hostent *
gethostbyaddr_rr(char * addr,int len,int type)
{
GETHOSTBYADDR_CMD cmd;
int * type_p;
	cmd.cmd.retry = (int(*)())gethostbyaddr_rr_retry;
	cmd.cmd.proc =  (int(*)())gethostbyaddr_rr_proc;
	cmd.cmd.type = BLT_RESOLVE;
	cmd.cmd.name = d_alloc(len + sizeof(type));
	cmd.cmd.name_len = len + sizeof(type);
	cmd.h = 0;
	type_p = (int*)cmd.cmd.name;
	*type_p = type;
	type_p ++;
	memcpy(type_p,addr,len);

	bl_do(&cmd);

	d_f_ree(cmd.cmd.name);

	return cmd.h;
}


int
gethostbyaddr_rr_retry(BLACKLIST * bl)
{
int type;
int * ptr;
char * addr;
int addr_len;
int ret;

	lock_task(netutils_lock);
	sethostent_rr(0);
	addr_len = bl->name_len - sizeof(type);
	ptr = (int*)bl->name;
	type = *ptr;
	ptr ++;
	addr = (char*)ptr;
	if ( _gethostbyaddr_rr(addr,addr_len,type) )
		ret = BLS_OK;
	else	ret = BLS_ERROR;
	endhostent_rr();
	unlock_task(netutils_lock,"new_connect");

	return ret;
}

HOST_ENTRY * 
intr_gethostbyaddr_rr(HOST_ADDR a)
{
char * addr;
int len;
int type;
GETHOSTBYADDR_CMD cmd;
int * type_p;
HOST_ENTRY * machine2he_v4(struct hostent*);


	if ( a.type != HAT_V4 )
		er_panic("gethostbyaddr_rr");
	a.d.v4 = htonl(a.d.v4);
	addr = (char*)&a.d;
	len = sizeof(a.d.v4);
	type = AF_INET;

	cmd.cmd.retry = (int(*)())gethostbyaddr_rr_retry;
	cmd.cmd.proc =  (int(*)())gethostbyaddr_rr_proc;
	cmd.cmd.type = BLT_RESOLVE;
	cmd.cmd.name = d_alloc(len + sizeof(type));
	cmd.cmd.name_len = len + sizeof(type);
	cmd.h = 0;
	type_p = (int*)cmd.cmd.name;
	*type_p = type;
	type_p ++;
	memcpy(type_p,addr,len);

	bl_do(&cmd);

	d_f_ree(cmd.cmd.name);

	return machine2he_v4(cmd.h);
}


} // extern "C"