// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import <Foundation/Foundation.h>
#import <OmniBase/rcsid.h>
#import <OmniBase/system.h>

// This tool looks up a host entry by name or address and returns 0 for success (or a socket library error code or generic -1 on failure).  When successful, it outputs the canonical hostname followed by the list of IP addresses.

static int lookupHostname(const char *hostname);
static int lookupAddress(const char *addressString);
static int writeHostEntry(struct hostent *hostEntry);
static void writeCanonicalHostname(const char *hostname);
static void writeHostAddress(unsigned long int address);
static int usage(void);

#ifdef WIN32
static BOOL WindowsStartup(void);
static void WindowsCleanup(void);
#import <fcntl.h>
#import <io.h>
#import <stdio.h>
#import <stdlib.h>
#endif

RCS_ID("$Header: /Network/Source/CVS/OmniGroup/OmniNetworking/ONGetHostEntry.tproj/ONGetHostEntry_main.m,v 1.7 1999/03/30 21:25:24 kc Exp $");

static const char *toolName;

int main(int argc, const char *argv[])
{
    int returnValue;

    toolName = argv[0];

    if (argc != 3)
        return usage();

#ifdef WIN32
    if (!WindowsStartup()) {
        fprintf(stderr, "%s: unable to find a suitable WinSock DLL (WinSock 2.0 compatibility required)", toolName);
        return -1;
    }

    // In order to write binary data to stdout, it must be in binary mode on windows
    if (setmode(fileno(stdout),_O_BINARY) == -1 )
        fprintf (stderr,"Couldn't setmode on stdout\n");

#endif

    switch (argv[1][0]) {
        case 'n': // name
            returnValue = lookupHostname(argv[2]);
            break;
        case 'a': // address
            returnValue = lookupAddress(argv[2]);
            break;
        default:
            returnValue = usage();
            break;
    }

#ifdef WIN32
    WindowsCleanup();
#endif

    return returnValue;
}

static int lookupHostname(const char *hostname)
{
    unsigned long int address;

    address = inet_addr(hostname);
    if (address != (unsigned long int)-1) {
        // Oh ho!  They gave us an IP number in dotted quad notation!  I guess we'll return the dotted quad as the canonical hostname, and the converted address as the address.
        // (We're not returning the real canonical hostname because it might return more addresses, and that wouldn't be what the user want since they specifically specified a single address.)
        writeCanonicalHostname(hostname);
        writeHostAddress(address);
        return 0;
    }

    // Attempt to get all of the addresses for the specified host
    return writeHostEntry(gethostbyname(hostname));
}

static int lookupAddress(const char *addressString)
{
    unsigned long int address;

    address = inet_addr(addressString);
    if (address == (unsigned long int)-1) {
        // Hey, that's no dotted quad address
        return usage();
    }

    // Attempt to get all of the addresses for the specified host
    return writeHostEntry(gethostbyaddr((char *)&address, sizeof(address), AF_INET));
}

static int writeHostEntry(struct hostent *hostEntry)
{
    unsigned long int address;
    unsigned int entryIndex;

    if (!hostEntry)
        return h_errno;

    writeCanonicalHostname(hostEntry->h_name);

    if (hostEntry->h_addrtype != AF_INET || hostEntry->h_length != sizeof(struct in_addr))
        // The host has no IP address.
        return 0;

    // Print out all of the addresses that we got back for ONHost to grab
    for (entryIndex = 0; hostEntry->h_addr_list[entryIndex]; entryIndex++) {
        address = ((struct in_addr *)hostEntry->h_addr_list[entryIndex])->s_addr;
        writeHostAddress(address);
    }
    return 0;
}

static void writeCanonicalHostname(const char *hostname)
{
    int hostnameLength;

    if (!hostname)
        hostname = "";
    hostnameLength = strlen(hostname);
    fwrite(&hostnameLength, sizeof(hostnameLength), 1, stdout);
    fwrite(hostname, hostnameLength, 1, stdout);
}

static void writeHostAddress(unsigned long int address)
{
    fwrite(&address, sizeof(address), 1, stdout);
}

static int usage()
{
    fprintf(stderr, "usage:\t%s name hostname\n\t%s address n.n.n.n\n", toolName, toolName);
    return -1;
}

#ifdef WIN32

static BOOL WindowsStartup(void)
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int startupErrorCode;

    wVersionRequested = MAKEWORD(1, 1);  // Changed from 2.0 because Win95-OSR2 only actually has 1.1.

    startupErrorCode = WSAStartup(wVersionRequested, &wsaData);

    // Confirm that the WinSock DLL supports 1.1.  Note that if the DLL supports versions greater than 1.1 in addition to 1.1, it will still return 2.0 in wVersion since that is the version we requested.

    if (LOBYTE(wsaData.wVersion) < 1 && HIBYTE(wsaData.wVersion) < 1) {
        fprintf(stderr, "Warning, %s using older version of WinSock (%d.%d)", toolName, LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
        return YES;
    }


    if (startupErrorCode != 0) {
        return NO;
    }

    // The WinSock DLL is acceptable.
    return YES;
}

static void WindowsCleanup(void)
{
    WSACleanup();
}

#endif
