/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
		Atsushi MURAMATSU <muramatsu@s-cradle.com>
 
	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	<netdb.h>
#include	<errno.h>
#include	"task.h"
#include	"lock_level.h"

SEM resolv_lock;

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


int
_endhostent_r()
{
	h_errno = 0;
	endhostent();
	return h_errno;
}


int
_sethostent_r(int stayopen)
{
	h_errno = 0;
	sethostent(stayopen);
	return h_errno;
}



  
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 = 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;
}

struct hostent *
gethostent_r(struct hostent *dest, void *buf, size_t buf_size, int *error)
{
     struct hostent *host, *result = NULL;

    lock_task(resolv_lock);
    h_errno = 0;
    host = gethostent();
    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);
    h_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);
    h_errno = 0;
    host = gethostbyaddr(addr, len, type);
    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;
}
