/**********************************************************************
 
	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.

**********************************************************************/


#include	<winsock2.h>
#include	<errno.h>
#include	"task.h"
#include	"lock_level.h"
#include	"netutils.h"

SEM resolv_lock;

void
init_resolv()
{
	resolv_lock = new_lock(LL_RESOLV);
}


int
_endhostent_r()
{
	return 0;
}


int
_sethostent_r(int stayopen)
{
	return 0;
}




static char *
copy_str_array(char **dest, char **src, char *buf, char *buf_end)
{
    int i;
    
    for (i=0; src[i]!=NULL; i++)
	buf += sizeof(char *);
    buf += sizeof(char*);
    if (buf >= buf_end)
	return NULL;
    
    for (i=0; src[i]!=NULL; i++) {
	dest[i] = buf;
	buf += strlen(src[i]) + 1;
	if (buf >= buf_end)
	    return NULL;
	strcpy(dest[i], src[i]);
    }
    dest[i] = NULL;
    return buf;
}


static char *
copy_addr_array(char **dest, char **src, char *buf, char *buf_end, int len)
{
    int i;
    
    for (i=0; src[i]!=NULL; i++)
	buf += sizeof(char *);
    buf += sizeof(char*);
    if (buf >= buf_end)
	return NULL;
    
    for (i=0; src[i]!=NULL; i++) {
	dest[i] = buf;
	buf += len;
	if (buf >= buf_end)
	    return NULL;
	memcpy(dest[i], src[i], len);
    }
    dest[i] = NULL;
    return buf;
}

int
copy_hostent(struct hostent *dest, struct hostent *src,
	     char *buf, int buf_size)
{
    char *buf_end = buf + buf_size;

    dest->h_name = buf;
    buf += strlen(src->h_name) + 1;
    if (buf >= buf_end)
	return -1;
    strcpy(dest->h_name, src->h_name);
    
    dest->h_aliases = (char**)buf;
    if ((buf = copy_str_array(dest->h_aliases, src->h_aliases,
			      buf, buf_end)) == NULL )
	return -1;
    dest->h_addrtype = src->h_addrtype;
    dest->h_length = src->h_length;
    dest->h_addr_list = (char **)buf;
    if ((buf = copy_addr_array(dest->h_addr_list, src->h_addr_list,
			      buf, buf_end, src->h_length)) == NULL )
	return -1;
    return 0;
}

/* fouce push "localhost" ,as windows2000 dont return "localhost" in hostent.h_aliases to 127.0.0.1 */
void push_localhost_to_aliases(struct hostent *host){
	int i;
	int is_localhost_exists;

	is_localhost_exists=0;
	if(strcmp(host->h_name, "localhost") == 0){
		is_localhost_exists = 1;
	}
	else{
		for(i=0;host->h_aliases[i];++i){
			if(strcmp(host->h_aliases[i], "localhost") == 0){
				is_localhost_exists = 1;
				break;
			}
		}
	}
	if(!is_localhost_exists){
		static char *aliases[10];
		for(i=0;host->h_aliases[i];++i){
			aliases[i] = host->h_aliases[i];
		}
		aliases[i] = "localhost";
		aliases[i+1] = 0;
		host->h_aliases = aliases;
	}
}

struct hostent *___gethostbyaddr(const char *ip){
	struct hostent *host = gethostbyaddr(ip, 4, AF_INET);
	if(0x0100007f == *(unsigned long*)ip){
		push_localhost_to_aliases(host);
	}
	return host;
}

struct hostent *
gethostent_r(struct hostent *dest, void *buf, size_t buf_size, int *error)
{
	struct hostent *host, *result = NULL;
	unsigned long localip;
	lock_task(resolv_lock);
	localip = get_localhostip();
	
	errno = 0;
	host = ___gethostbyaddr((const char *)&localip);
	
	errno = h_errno;
	if (error != NULL)
		*error = h_errno;
	
	if (host != NULL) {
		if (copy_hostent(dest, host, buf, buf_size) == 0) {
			result = dest;
		}
		else {
			errno = ERANGE;
			if (error != NULL)
				*error = ERANGE;
		}
	}
	unlock_task(resolv_lock,"gethostent_r");

	return result;
}


struct hostent *
gethostbyname_r(const char *hostname, struct hostent *dest,
		void *buf, size_t buf_size, int *error)
{
    struct hostent *host, *result = NULL;

    lock_task(resolv_lock);
    errno = 0;
    host = gethostbyname(hostname);
    errno = h_errno;
    if (error != NULL)
		*error = h_errno;
    
    if (host != NULL) {
		if (copy_hostent(dest, host, buf, buf_size) == 0){
			result = dest;
		}
		else {
			errno = ERANGE;
			if (error != NULL)
				*error = ERANGE;
		}
    }
    unlock_task(resolv_lock,"gethostbyname_r");
    
    return result;
}

struct hostent *
_gethostbyaddr_r(const char *addr, int len, int	type,
		 struct hostent *dest, void *buf, size_t buf_size, int *error)
{
    struct hostent *host, *result = NULL;

    lock_task(resolv_lock);

    errno = 0;
    host = ___gethostbyaddr(addr);
	
    errno = h_errno;
    if (error != NULL)
		*error = h_errno;
    
    if (host != NULL) {
		if (copy_hostent(dest, host, buf, buf_size) == 0){
			result = dest;
		}
		else {
			errno = ERANGE;
			if (error != NULL)
				*error = ERANGE;
		}
    }

    unlock_task(resolv_lock,"_gethostbyaddr_r");

	return result;

}    
