/*
 *        Unofficial release 1.3.0
 *                B I N G
 *
 */

/* $Id: bing_misc.c,v 1.5 1999/10/16 07:07:58 fgouget Exp $ */

#include <stdio.h>
#include <string.h>

/* types.h provides u_short on HPUX10 and Solaris */
#include <sys/types.h>

#ifdef WIN32
#include <winsock.h>
#else
#include <netdb.h>
#endif

#include "bing_defs.h"
#include "bing_misc.h"

#ifndef INADDR_NONE
/* This does not seem to be defined on Solaris */
#define INADDR_NONE              (u_long)0xffffffff
#endif

#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK          (u_long)0x7F000001
#endif

#ifdef FAKE_NET
/* (!!) for testing (without a connection) */
typedef struct {
    char* name;
    char* addr;
} pseudo_hosts;

#define NB_PSEUDO_HOSTS 13
static pseudo_hosts hosts[NB_PSEUDO_HOSTS]={
    {"alcatraz","208.199.87.55"},
    {"alcatraz.metaintegration.net","208.199.87.55"},
    {"host1_1","192.168.1.1"},
    {"host1_2","192.168.1.2"},
    {"host1_3","192.168.1.3"},
    {"host1_4","192.168.1.4"},
    {"host1_5","192.168.1.5"},
    {"host2_1","192.168.2.1"},
    {"host2_2","192.168.2.2"},
    {"host2_3","192.168.2.3"},
    {"host2_4","192.168.2.4"},
    {"host2_5","192.168.2.5"},
    {"alcatraz","127.0.0.1"}
    };

struct hostent* __gethostbyname(char* hostname)
{
    static struct hostent he;
    static struct in_addr addr;
    static struct in_addr* he_addr[2];
    int i;
    for (i=0;i<NB_PSEUDO_HOSTS;i++) {
        if (strcmp(hosts[i].name,hostname)==0) {
            he.h_name=hosts[i].name;
            he.h_aliases=NULL;
            he.h_addrtype=AF_INET;
            he.h_length=1;
            addr.S_un.S_addr=inet_addr(hosts[i].addr);
            he_addr[0]=&addr;
            he_addr[1]=NULL;
            he.h_addr_list=(char**)&he_addr;
            return &he;
        }
    }
    return NULL;
}

struct hostent* __gethostbyaddr(char* host_addr,int size,int domain)
{
    static struct hostent he;
    static struct in_addr addr;
    static struct in_addr* he_addr[2];
    char* ip_str;
    int i;

    if (domain!=AF_INET)
        return NULL;

    ip_str=inet_ntoa(*(struct in_addr*)host_addr);
    for (i=0;i<NB_PSEUDO_HOSTS;i++) {
        if (strcmp(hosts[i].addr,ip_str)==0) {
            he.h_name=hosts[i].name;
            he.h_aliases=NULL;
            he.h_addrtype=AF_INET;
            he.h_length=1;
            addr.S_un.S_addr=inet_addr(hosts[i].addr);
            he_addr[0]=&addr;
            he_addr[1]=NULL;
            he.h_addr_list=(char**)&he_addr;
            return &he;
        }
    }
    return NULL;
}

#define gethostbyname(name) __gethostbyname(name)
#define gethostbyaddr(addr,size,domain) __gethostbyaddr(addr,size,domain)
#endif /* FAKE_NET */


int addrcmp(struct sockaddr* addr1, struct sockaddr* addr2)
{
    if (addr1->sa_family!=addr2->sa_family)
        return addr1->sa_family-addr2->sa_family;
    switch (addr1->sa_family) {
    case AF_INET:
        return memcmp(&(SOCKADDR_IN(addr1)->sin_addr),
                      &(SOCKADDR_IN(addr2)->sin_addr),
                      sizeof(struct in_addr)
                     );
        break;
    default:
        return memcmp(addr1,addr2,sizeof(struct sockaddr));
    }
}

int addrcmp2(struct sockaddr* addr1, unsigned short family, char* addr2)
{
    if (addr1->sa_family!=family)
        return addr1->sa_family-family;
    switch (addr1->sa_family) {
    case AF_INET:
        return memcmp(&(SOCKADDR_IN(addr1)->sin_addr),
                      addr2,
                      sizeof(struct in_addr)
                     );
        break;
    default:
        return memcmp(addr1,addr2,sizeof(struct sockaddr));
    }
}

void addrset(struct sockaddr* sa,struct in_addr ia,unsigned short ip)
{
    struct sockaddr_in* sai=(struct sockaddr_in*)sa;
    sai->sin_family=PF_INET;
    sai->sin_port=ip;
    sai->sin_addr=ia;
}

struct hostent* getlocalhost(int domain)
{
    struct in_addr addr;
    
    if (domain!=AF_INET)
        return NULL;

    /* (!!) this seems to be specific to linux, on other platforms it would be addr.S_un.S_addr */
    addr.s_addr=INADDR_LOOPBACK;
    return gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}

int host_name2addr(char* host_string, struct sockaddr* host_addr)
{
    unsigned long int addr;

    addr=inet_addr(host_string);
    if (addr==INADDR_NONE) {
        struct hostent *he;

        he=gethostbyname(host_string);
        if (he==NULL) {
            return -1;
        } else if (he->h_addrtype!=AF_INET) {
            return -1;
        } else {
            host_addr->sa_family=AF_INET;
            /* the port *must* be initialized, even in icmp mode ! */
            SOCKADDR_IN(host_addr)->sin_port=0;
            memcpy(&SOCKADDR_IN(host_addr)->sin_addr, he->h_addr, sizeof(SOCKADDR_IN(host_addr)->sin_addr));
        }
    } else {
        host_addr->sa_family=AF_INET;
        /* the port *must* be initialized, even in icmp mode ! */
        SOCKADDR_IN(host_addr)->sin_port=0;
        memcpy(&SOCKADDR_IN(host_addr)->sin_addr,&addr,sizeof(SOCKADDR_IN(host_addr)->sin_addr));
    }
    return 0;
}

char* host_addr2name(struct sockaddr* host_addr, int resolve)
{
    static char host_string[15+1+1+64+1+1];
    char* ip_str;

    switch (host_addr->sa_family) {
    case AF_INET:
        ip_str=inet_ntoa(SOCKADDR_IN(host_addr)->sin_addr);
        if (!resolve) {
            strncpy(host_string,ip_str,sizeof(host_string));
        } else {
            struct hostent *he;

            /* (!!) check out how gethostbyaddr works to find a more generic way of doing this */
            he=gethostbyaddr((const char*)&(SOCKADDR_IN(host_addr)->sin_addr),4,AF_INET);
            if (he!=NULL)
                snprintf(snfargs(host_string,sizeof(host_string),"%s/%s"),
                     he->h_name,ip_str);
            else
                snprintf(snfargs(host_string,sizeof(host_string),"<unknown>/%s"),
                     ip_str);
        }
        host_string[sizeof(host_string)-1]='\0';
        return host_string;
    default:
        return "<unknown address type>";
    }
}
