/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS WinSock 2 API
 * FILE:        dll/win32/ws2_32/src/addrconv.c
 * PURPOSE:     Address and Port Conversion Support
 * PROGRAMMER:  Alex Ionescu (alex@relsoft.net)
 */

/* INCLUDES ******************************************************************/

#include <ws2_32.h>

#define NDEBUG
#include <debug.h>

/* DEFINES *******************************************************************/

#ifndef BE

/* DWORD network to host byte order conversion for little endian machines */
#define DN2H(dw) \
  ((((dw) & 0xFF000000L) >> 24) | \
   (((dw) & 0x00FF0000L) >> 8) | \
     (((dw) & 0x0000FF00L) << 8) | \
     (((dw) & 0x000000FFL) << 24))

/* DWORD host to network byte order conversion for little endian machines */
#define DH2N(dw) \
    ((((dw) & 0xFF000000L) >> 24) | \
     (((dw) & 0x00FF0000L) >> 8) | \
     (((dw) & 0x0000FF00L) << 8) | \
     (((dw) & 0x000000FFL) << 24))

/* WORD network to host order conversion for little endian machines */
#define WN2H(w) \
    ((((w) & 0xFF00) >> 8) | \
     (((w) & 0x00FF) << 8))

/* WORD host to network byte order conversion for little endian machines */
#define WH2N(w) \
    ((((w) & 0xFF00) >> 8) | \
     (((w) & 0x00FF) << 8))

#else /* BE */

/* DWORD network to host byte order conversion for big endian machines */
#define DN2H(dw) \
    (dw)

/* DWORD host to network byte order conversion big endian machines */
#define DH2N(dw) \
    (dw)

/* WORD network to host order conversion for big endian machines */
#define WN2H(w) \
    (w)

/* WORD host to network byte order conversion for big endian machines */
#define WH2N(w) \
    (w)

#endif /* BE */

/* FUNCTIONS *****************************************************************/

/*
 * @implemented
 */
ULONG
WSAAPI
inet_addr(IN  CONST CHAR FAR* cp)
{
    register u_long val, base, n;
    register unsigned char c;
    u_long parts[4], *pp = parts;
    if (!cp) return INADDR_ANY;
    if (!isdigit(*cp)) return INADDR_NONE;

again:
    /*
     * Collect number up to ``.''.
     * Values are specified as for C:
     * 0x=hex, 0=octal, other=decimal.
     */
    val = 0; base = 10;
    if (*cp == '0') {
        if (*++cp == 'x' || *cp == 'X')
            base = 16, cp++;
        else
            base = 8;
    }
    while ((c = *cp)) {
        if (isdigit(c)) {
            val = (val * base) + (c - '0');
            cp++;
            continue;
        }
        if (base == 16 && isxdigit(c)) {
            val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A'));
            cp++;
            continue;
        }
        break;
    }
    if (*cp == '.') {
        /*
         * Internet format:
         *    a.b.c.d
         *    a.b.c    (with c treated as 16-bits)
         *    a.b    (with b treated as 24 bits)
         */
        if (pp >= parts + 4) return (INADDR_NONE);
        *pp++ = val;
        cp++;
        goto again;
    }
    /*
     * Check for trailing characters.
     */
    if (*cp && !isspace((UCHAR)*cp)) return (INADDR_NONE);

    *pp++ = val;
    /*
     * Concoct the address according to
     * the number of parts specified.
     */
    n = (u_long)(pp - parts);
    switch (n) {

    case 1:                /* a -- 32 bits */
        val = parts[0];
        break;

    case 2:                /* a.b -- 8.24 bits */
        val = (parts[0] << 24) | (parts[1] & 0xffffff);
        break;

    case 3:                /* a.b.c -- 8.8.16 bits */
        val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
            (parts[2] & 0xffff);
        break;

    case 4:                /* a.b.c.d -- 8.8.8.8 bits */
        val = (parts[0] << 24) | ((parts[1] & 0xff) << 16) |
              ((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
        break;

    default:
        return (INADDR_NONE);
    }
    val = htonl(val);
    return (val);
}

/*
 * @implemented
 */
CHAR FAR*
WSAAPI
inet_ntoa(IN IN_ADDR in)
{
    PWSPROCESS Process;
    PWSTHREAD Thread;
    INT ErrorCode;
    WSADATA WsaData;
    BOOL ManualLoad = FALSE;
    CHAR b[10];
    PCHAR p;
    DPRINT("inet_ntoa: %lx\n", in);

    /* Enter prolog */
    if ((ErrorCode = WsApiProlog(&Process, &Thread)) != ERROR_SUCCESS)
    {
        DPRINT("MANUAL LOAD\n");

        /* Only fail if the error wasn't related to a missing WSAStartup */
        if (ErrorCode != WSANOTINITIALISED)
        {
            /* Fail */
            SetLastError(ErrorCode);
            return NULL;
        }

        /* Apps aren't expected to call WSAStartup for this API, so we will */
        if ((ErrorCode = WSAStartup(MAKEWORD(2,2), &WsaData)) != ERROR_SUCCESS)
        {
            /* We failed */
            SetLastError(ErrorCode);
            return NULL;
        }

        /* Try the prolog again */
        ManualLoad = TRUE;
        if ((ErrorCode = WsApiProlog(&Process, &Thread)) != ERROR_SUCCESS)
        {
            /* Failed again... */
            WSACleanup();
            SetLastError(ErrorCode);
            return NULL;
        }
    }

    p = Thread->Buffer;
    _itoa(in.S_un.S_addr & 0xFF, b, 10);
    strcpy(p, b);
    _itoa((in.S_un.S_addr >> 8) & 0xFF, b, 10);
    strcat(p, ".");
    strcat(p, b);
    _itoa((in.S_un.S_addr >> 16) & 0xFF, b, 10);
    strcat(p, ".");
    strcat(p, b);
    _itoa((in.S_un.S_addr >> 24) & 0xFF, b, 10);
    strcat(p, ".");
    strcat(p, b);

    /* Cleanup the manual load */
    if (ManualLoad) WSACleanup();

    /* Return the buffer */
    return p;
}

/*
 * @implemented
 */
ULONG
WSAAPI
htonl(IN ULONG hostlong)
{
    return DH2N(hostlong);
}

/*
 * @implemented
 */
USHORT
WSAAPI
htons(IN USHORT hostshort)
{
    return WH2N(hostshort);
}

/*
 * @implemented
 */
ULONG
WSAAPI
ntohl(IN ULONG netlong)
{
    return DN2H(netlong);
}

/*
 * @implemented
 */
USHORT
WSAAPI
ntohs(IN USHORT netshort)
{
    return WN2H(netshort);
}

/*
 * @implemented
 */
INT
WSAAPI
WSAHtonl(IN SOCKET s,
         IN ULONG hostlong,
         OUT ULONG FAR* lpnetlong)
{
    INT ErrorCode;
    PWSSOCKET Socket;
    DPRINT("WSAHtonl: %p, %lx, %p\n", s, hostlong, lpnetlong);

    /* Check for WSAStartup */
    if ((ErrorCode = WsQuickProlog()) == ERROR_SUCCESS)
    {
        /* Make sure we got a parameter */
        if (!lpnetlong)
        {
            /* Fail */
            SetLastError(WSAEFAULT);
            return SOCKET_ERROR;
        }

        /* Get the Socket Context */
        if ((Socket = WsSockGetSocket(s)))
        {
            /* Check which byte order to use */
            if (Socket->CatalogEntry->ProtocolInfo.iNetworkByteOrder ==
                LITTLEENDIAN)
            {
                /* No conversion needed */
                *lpnetlong = hostlong;
            }
            else
            {
                /* Use a swap */
                *lpnetlong = DN2H(hostlong);
            }

            /* Dereference the socket */
            WsSockDereference(Socket);

            /* Return success */
            return ERROR_SUCCESS;
        }
        else
        {
            /* Set the error code */
            ErrorCode = WSAENOTSOCK;
        }
    }

    /* Return with error */
    SetLastError(ErrorCode);
    return SOCKET_ERROR;
}

/*
 * @implemented
 */
INT
WSAAPI
WSAHtons(IN SOCKET s,
         IN USHORT hostshort,
         OUT USHORT FAR* lpnetshort)
{
    INT ErrorCode;
    PWSSOCKET Socket;
    DPRINT("WSAHtons: %p, %lx, %p\n", s, hostshort, lpnetshort);

    /* Check for WSAStartup */
    if ((ErrorCode = WsQuickProlog()) == ERROR_SUCCESS)
    {
        /* Make sure we got a parameter */
        if (!lpnetshort)
        {
            /* Fail */
            SetLastError(WSAEFAULT);
            return SOCKET_ERROR;
        }

        /* Get the Socket Context */
        if ((Socket = WsSockGetSocket(s)))
        {
            /* Check which byte order to use */
            if (Socket->CatalogEntry->ProtocolInfo.iNetworkByteOrder ==
                LITTLEENDIAN)
            {
                /* No conversion needed */
                *lpnetshort = hostshort;
            }
            else
            {
                /* Use a swap */
                *lpnetshort = WN2H(hostshort);
            }

            /* Dereference the socket */
            WsSockDereference(Socket);

            /* Return success */
            return ERROR_SUCCESS;
        }
        else
        {
            /* Set the error code */
            ErrorCode = WSAENOTSOCK;
        }
    }

    /* Return with error */
    SetLastError(ErrorCode);
    return SOCKET_ERROR;
}

/*
 * @implemented
 */
INT
WSAAPI
WSANtohl(IN SOCKET s,
         IN ULONG netlong,
         OUT ULONG FAR* lphostlong)
{
    INT ErrorCode;
    PWSSOCKET Socket;
    DPRINT("WSANtohl: %p, %lx, %p\n", s, netlong, lphostlong);

    /* Check for WSAStartup */
    if ((ErrorCode = WsQuickProlog()) == ERROR_SUCCESS)
    {
        /* Make sure we got a parameter */
        if (!lphostlong)
        {
            /* Fail */
            SetLastError(WSAEFAULT);
            return SOCKET_ERROR;
        }

        /* Get the Socket Context */
        if ((Socket = WsSockGetSocket(s)))
        {
            /* Check which byte order to use */
            if (Socket->CatalogEntry->ProtocolInfo.iNetworkByteOrder ==
                LITTLEENDIAN)
            {
                /* No conversion needed */
                *lphostlong = netlong;
            }
            else
            {
                /* Use a swap */
                *lphostlong = DN2H(netlong);
            }

            /* Dereference the socket */
            WsSockDereference(Socket);

            /* Return success */
            return ERROR_SUCCESS;
        }
        else
        {
            /* Set the error code */
            ErrorCode = WSAENOTSOCK;
        }
    }

    /* Return with error */
    SetLastError(ErrorCode);
    return SOCKET_ERROR;
}

/*
 * @implemented
 */
INT
WSAAPI
WSANtohs(IN SOCKET s,
         IN USHORT netshort,
         OUT USHORT FAR* lphostshort)
{
    INT ErrorCode;
    PWSSOCKET Socket;
    DPRINT("WSANtohs: %p, %lx, %p\n", s, netshort, lphostshort);

    /* Check for WSAStartup */
    if ((ErrorCode = WsQuickProlog()) == ERROR_SUCCESS)
    {
        /* Make sure we got a parameter */
        if (!lphostshort)
        {
            /* Fail */
            SetLastError(WSAEFAULT);
            return SOCKET_ERROR;
        }

        /* Get the Socket Context */
        if ((Socket = WsSockGetSocket(s)))
        {
            /* Check which byte order to use */
            if (Socket->CatalogEntry->ProtocolInfo.iNetworkByteOrder ==
                LITTLEENDIAN)
            {
                /* No conversion needed */
                *lphostshort = netshort;
            }
            else
            {
                /* Use a swap */
                *lphostshort = WN2H(netshort);
            }

            /* Dereference the socket */
            WsSockDereference(Socket);

            /* Return success */
            return ERROR_SUCCESS;
        }
        else
        {
            /* Set the error code */
            ErrorCode = WSAENOTSOCK;
        }
    }

    /* Return with error */
    SetLastError(ErrorCode);
    return SOCKET_ERROR;
}

