/*++

Copyright (c) 1996-2001 Microsoft Corporation

Module Name:

    straddr.c

Abstract:

    Domain Name System (DNS) Library

    Routines to string to\from address conversions.

Author:

    Jim Gilroy (jamesg)     December 1996

Revision History:

    jamesg      June 2000       New IP6 parsing.
    jamesg      Oct 2000        Created this module.

--*/


#include "local.h"
#include "ws2tcpip.h"   // IP6 inaddr definitions



//
//  String to address
//

BOOL
Dns_Ip6StringToAddress_A(
    OUT     PIP6_ADDRESS    pIp6Addr,
    IN      PCSTR           pString
    )
/*++

Routine Description:

    Convert string to IP6 address.

Arguments:

    pAddress -- ptr to IP6 address to be filled in

    pString -- string with IP6 address

Return Value:

    TRUE if successful.
    FALSE otherwise.

--*/
{
    DNS_STATUS  status;
    PCHAR       pstringEnd = NULL;


    DNSDBG( PARSE2, (
        "Dns_Ip6StringToAddress_A( %s )\n",
        pString ));

    //
    //  convert to IP6 address
    //

    status = RtlIpv6StringToAddressA(
                pString,
                & pstringEnd,
                (PIN6_ADDR) pIp6Addr );

    return( status == NO_ERROR  &&  *pstringEnd==0 );
}



BOOL
Dns_Ip6StringToAddressEx_A(
    OUT     PIP6_ADDRESS    pIp6Addr,
    IN      PCSTR           pchString,
    IN      DWORD           dwStringLength
    )
/*++

Routine Description:

    Convert string to IP6 address.

    This version handles non-NULL-terminated strings
    for DNS server file load.

Arguments:

    pAddress -- ptr to IP6 address to be filled in

    pchString -- string with IP6 address

    dwStringLength -- string length

Return Value:

    TRUE if successful.
    FALSE otherwise.

--*/
{
    CHAR        tempBuf[ IP6_ADDRESS_STRING_BUFFER_LENGTH ];
    PCSTR       pstring;
    
    DNSDBG( PARSE2, (
        "Dns_Ip6StringToAddressEx_A( %.*s )\n"
        "\tpchString = %p\n",
        dwStringLength,
        pchString,
        pchString ));

    //
    //  copy string if given length
    //  if no length assume NULL terminated
    //

    pstring = pchString;

    if ( dwStringLength )
    {
        DWORD   bufLength = IP6_ADDRESS_STRING_BUFFER_LENGTH;

        if ( ! Dns_StringCopy(
                    tempBuf,
                    & bufLength,
                    (PCHAR) pstring,
                    dwStringLength,
                    DnsCharSetAnsi,
                    DnsCharSetAnsi ) )
        {
            return( FALSE );
        }
        pstring = tempBuf;
    }

    //  convert to IP6 address

    return  Dns_Ip6StringToAddress_A(
                pIp6Addr,
                pstring );
}



BOOL
Dns_Ip6StringToAddress_W(
    OUT     PIP6_ADDRESS    pIp6Addr,
    IN      PCWSTR          pwString
    )
/*++

Routine Description:

    Build IP6 address from wide string.

Arguments:

    pwString -- unicode IP6 string

    pIp6Addr -- addr to recv IP6 address

Return Value:

    TRUE if successful conversion.
    FALSE on bad string.

--*/
{
    DNS_STATUS  status;
    PWCHAR      pstringEnd = NULL;

    DNSDBG( PARSE2, (
        "Dns_Ip6StringToAddress_W( %S )\n",
        pwString ));

    //
    //  convert to IP6 address
    //

    status = RtlIpv6StringToAddressW(
                pwString,
                & pstringEnd,
                (PIN6_ADDR) pIp6Addr );

    return( status == NO_ERROR  &&  *pstringEnd==0 );
}



BOOL
Dns_Ip4StringToAddress_A(
    OUT     PIP4_ADDRESS    pIp4Addr,
    IN      PCSTR           pString
    )
/*++

Routine Description:

    Build IP4 address from narrow string.

Arguments:

    pIp4Addr -- addr to recv IP6 address

    pString -- unicode IP4 string

Return Value:

    TRUE if successful conversion.
    FALSE on bad string.

--*/
{
    IP4_ADDRESS ip;

    //  if inet_addr() returns error, verify then error out

    ip = inet_addr( pString );

    if ( ip == INADDR_BROADCAST &&
        strcmp( pString, "255.255.255.255" ) != 0 )
    {
        return( FALSE );
    }

    *pIp4Addr = ip;

    return( TRUE );
}



BOOL
Dns_Ip4StringToAddressEx_A(
    OUT     PIP4_ADDRESS    pIp4Addr,
    IN      PCSTR           pchString,
    IN      DWORD           dwStringLength
    )
/*++

Routine Description:

    Build IP4 address from narrow string.

    This version handles non-NULL terminated strings

Arguments:

    pIp4Addr -- addr to recv IP6 address

    pString -- unicode IP4 string

    dwStringLength -- string length; 0 if NULL terminated

Return Value:

    TRUE if successful conversion.
    FALSE on bad string.

--*/
{
    CHAR        tempBuf[ IP4_ADDRESS_STRING_BUFFER_LENGTH ];
    PCSTR       pstring;
    
    DNSDBG( PARSE2, (
        "Dns_Ip4StringToAddressEx_A( %.*s )\n"
        "\tpchString = %p\n",
        dwStringLength,
        pchString,
        pchString ));

    //
    //  copy string if given length
    //  if no length assume NULL terminated
    //

    pstring = pchString;

    if ( dwStringLength )
    {
        DWORD   bufLength = IP4_ADDRESS_STRING_BUFFER_LENGTH;

        if ( ! Dns_StringCopy(
                    tempBuf,
                    & bufLength,
                    (PCHAR) pstring,
                    dwStringLength,
                    DnsCharSetAnsi,
                    DnsCharSetAnsi ) )
        {
            return( FALSE );
        }
        pstring = tempBuf;
    }

    return  Dns_Ip4StringToAddress_A(
                pIp4Addr,
                pstring );
}



BOOL
Dns_Ip4StringToAddress_W(
    OUT     PIP4_ADDRESS    pIp4Addr,
    IN      PCWSTR          pwString
    )
/*++

Routine Description:

    Build IP4 address from wide string.

Arguments:

    pIp4Addr -- addr to recv IP6 address

    pwString -- unicode IP6 string

Return Value:

    TRUE if successful conversion.
    FALSE on bad string.

--*/
{
    CHAR        bufAddr[ IP4_ADDRESS_STRING_BUFFER_LENGTH ];
    DWORD       bufLength = IP4_ADDRESS_STRING_BUFFER_LENGTH;

    //  convert to narrow string
    //      - UTF8 quicker and just fine for numeric

    if ( ! Dns_StringCopy(
                bufAddr,
                & bufLength,
                (PCHAR) pwString,
                0,          // length unknown
                DnsCharSetUnicode,
                DnsCharSetUtf8
                ) )
    {
        return( FALSE );
    }

    return  Dns_Ip4StringToAddress_A(
                pIp4Addr,
                bufAddr );
}



//
//  Combined IP4/IP6 string-to-address
//

BOOL
Dns_StringToAddress_W(
    OUT     PCHAR           pAddrBuf,
    IN OUT  PDWORD          pBufLength,
    IN      PCWSTR          pString,
    IN OUT  PDWORD          pAddrFamily
    )
/*++

Routine Description:

    Build address (IP4 or IP6) from address string.

Arguments:

    pAddrBuf -- buffer to receive address

    pBufLength -- ptr to address length
        input   - length of buffer
        output  - length of address found

    pString -- address string

    pAddrFamily -- ptr to address family
        input   - zero for any family or particular family to check
        output  - family found;  zero if no conversion

Return Value:

    TRUE if successful.
    FALSE on error.  GetLastError() for status.

--*/
{
    return  Dns_StringToAddressEx(
                pAddrBuf,
                pBufLength,
                (PCSTR) pString,
                pAddrFamily,
                TRUE,       // unicode
                FALSE       // forward
                );
}

BOOL
Dns_StringToAddress_A(
    OUT     PCHAR           pAddrBuf,
    IN OUT  PDWORD          pBufLength,
    IN      PCSTR           pString,
    IN OUT  PDWORD          pAddrFamily
    )
{
    return  Dns_StringToAddressEx(
                pAddrBuf,
                pBufLength,
                pString,
                pAddrFamily,
                FALSE,      // ANSI
                FALSE       // forward
                );
}



//
//  Address to string
//

PWCHAR
Dns_Ip6AddressToString_W(
    OUT     PWCHAR          pwString,
    IN      PIP6_ADDRESS    pIp6Addr
    )
/*++

Routine Description:

    Convert IP6 address to string format.

Arguments:

    pwString -- buffer to hold string;  MUST be at least
        IPV6_ADDRESS_STRING_LENGTH+1 in length

    pAddress -- IP6 address to convert to string

Return Value:

    Ptr to next location in buffer (the terminating NULL).

--*/
{
    //  DCR:  could be macro

    return  RtlIpv6AddressToStringW(
                (PIN6_ADDR) pIp6Addr,
                pwString );
}



PCHAR
Dns_Ip6AddressToString_A(
    OUT     PCHAR           pchString,
    IN      PIP6_ADDRESS    pIp6Addr
    )
/*++

Routine Description:

    Convert IP6 address to string format.

Arguments:

    pchString -- buffer to hold string;  MUST be at least
        IPV6_ADDRESS_STRING_LENGTH+1 in length

    pAddress -- IP6 address to convert to string

Return Value:

    Ptr to next location in buffer (the terminating NULL).

--*/
{
    //  DCR:  could be macro

    return  RtlIpv6AddressToStringA(
                (PIN6_ADDR) pIp6Addr,
                pchString );
}



//
//  Address to string -- IP4
//

PWCHAR
Dns_Ip4AddressToString_W(
    OUT     PWCHAR          pwString,
    IN      PIP4_ADDRESS    pIp4Addr
    )
/*++

Routine Description:

    Convert IP4 address to string format.

Arguments:

    pwString -- buffer to hold string;  MUST be at least
        IPV6_ADDRESS_STRING_LENGTH+1 in length

    pAddress -- IP4 address to convert to string

Return Value:

    Ptr to next location in buffer (the terminating NULL).

--*/
{
    IP4_ADDRESS ip = *pIp4Addr;

    //
    //  convert IP4 address to string
    //      - address is in net order, lead byte in low memory
    //

    pwString += wsprintfW(
                    pwString,
                    L"%u.%u.%u.%u",
                    (UCHAR) (ip & 0x000000ff),
                    (UCHAR) ((ip & 0x0000ff00) >> 8),
                    (UCHAR) ((ip & 0x00ff0000) >> 16),
                    (UCHAR) ((ip & 0xff000000) >> 24)
                    );

    return( pwString );
}



PCHAR
Dns_Ip4AddressToString_A(
    OUT     PCHAR           pString,
    IN      PIP4_ADDRESS    pIp4Addr
    )
/*++

Routine Description:

    Convert IP4 address to string format.

Arguments:

    pchString -- buffer to hold string;  MUST be at least
        IPV6_ADDRESS_STRING_LENGTH+1 in length

    pAddress -- IP4 address to convert to string

Return Value:

    Ptr to next location in buffer (the terminating NULL).

--*/
{
    IP4_ADDRESS ip = *pIp4Addr;

    //
    //  convert IP4 address to string
    //      - address is in net order, lead byte in low memory
    //

    pString += sprintf(
                    pString,
                    "%u.%u.%u.%u",
                    (UCHAR) (ip & 0x000000ff),
                    (UCHAR) ((ip & 0x0000ff00) >> 8),
                    (UCHAR) ((ip & 0x00ff0000) >> 16),
                    (UCHAR) ((ip & 0xff000000) >> 24)
                    );

    return( pString );
}



//
//  Address-to-string -- combined IP4/6
//

PCHAR
Dns_AddressToString_A(
    OUT     PCHAR           pchString,
    IN OUT  PDWORD          pStringLength,
    IN      PBYTE           pAddr,
    IN      DWORD           AddrLength,
    IN      DWORD           AddrFamily
    )
/*++

Routine Description:

    Convert address to string format.

Arguments:

    pchString -- buffer to hold string;  MUST be at least
        IPV6_ADDRESS_STRING_LENGTH+1 in length

    pStringLength -- string buffer length

    pAddr -- ptr to address

    AddrLength -- address length

    AddrFamily -- address family (AF_INET, AF_INET6)

Return Value:

    Ptr to next location in buffer (the terminating NULL).
    NULL if no conversion.

--*/
{
    DWORD   length = *pStringLength;

    //  dispatch to conversion routine for this type

    if ( AddrFamily == AF_INET )
    {
        if ( length < IP_ADDRESS_STRING_LENGTH+1 )
        {
            length = IP_ADDRESS_STRING_LENGTH+1;
            goto Failed;
        }
        return  Dns_Ip4AddressToString_A(
                    pchString,
                    (PIP4_ADDRESS) pAddr );
    }

    if ( AddrFamily == AF_INET6 )
    {
        if ( length < IP6_ADDRESS_STRING_LENGTH+1 )
        {
            length = IP6_ADDRESS_STRING_LENGTH+1;
            goto Failed;
        }
        return  Dns_Ip6AddressToString_A(
                    pchString,
                    (PIP6_ADDRESS) pAddr );
    }

Failed:

    *pStringLength = length;

    return  NULL;
}



//
//  Reverse lookup address-to-name IP4
//

PCHAR
Dns_Ip4AddressToReverseName_A(
    OUT     PCHAR           pBuffer,
    IN      IP_ADDRESS      IpAddress
    )
/*++

Routine Description:

    Write reverse lookup name, given corresponding IP

Arguments:

    pBuffer -- ptr to buffer for reverse lookup name;
        MUST contain at least DNS_MAX_REVERSE_NAME_BUFFER_LENGTH bytes

    IpAddress -- IP address to create

Return Value:

    Ptr to next location in buffer.

--*/
{
    DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseName_A()\n" ));

    //
    //  write digits for each octect in IP address
    //      - note, it is in net order so lowest octect, is in highest memory
    //

    pBuffer += sprintf(
                    pBuffer,
                    "%u.%u.%u.%u.in-addr.arpa.",
                    (UCHAR) ((IpAddress & 0xff000000) >> 24),
                    (UCHAR) ((IpAddress & 0x00ff0000) >> 16),
                    (UCHAR) ((IpAddress & 0x0000ff00) >> 8),
                    (UCHAR) (IpAddress & 0x000000ff) );

    return( pBuffer );
}



PWCHAR
Dns_Ip4AddressToReverseName_W(
    OUT     PWCHAR          pBuffer,
    IN      IP_ADDRESS      IpAddress
    )
/*++

Routine Description:

    Write reverse lookup name, given corresponding IP

Arguments:

    pBuffer -- ptr to buffer for reverse lookup name;
        MUST contain at least DNS_MAX_REVERSE_NAME_BUFFER_LENGTH wide chars

    IpAddress -- IP address to create

Return Value:

    Ptr to next location in buffer.

--*/
{
    DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseName_W()\n" ));

    //
    //  write digits for each octect in IP address
    //      - note, it is in net order so lowest octect, is in highest memory
    //

    pBuffer += wsprintfW(
                    pBuffer,
                    L"%u.%u.%u.%u.in-addr.arpa.",
                    (UCHAR) ((IpAddress & 0xff000000) >> 24),
                    (UCHAR) ((IpAddress & 0x00ff0000) >> 16),
                    (UCHAR) ((IpAddress & 0x0000ff00) >> 8),
                    (UCHAR) (IpAddress & 0x000000ff) );

    return( pBuffer );
}



PCHAR
Dns_Ip4AddressToReverseNameAlloc_A(
    IN      IP_ADDRESS      IpAddress
    )
/*++

Routine Description:

    Create reverse lookup name string, given corresponding IP.

    Caller must free the string.

Arguments:

    IpAddress -- IP address to create

Return Value:

    Ptr to new reverse lookup string.

--*/
{
    PCHAR   pch;
    PCHAR   pchend;

    DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseNameAlloc_A()\n" ));

    //
    //  allocate space for string
    //

    pch = ALLOCATE_HEAP( DNS_MAX_REVERSE_NAME_BUFFER_LENGTH );
    if ( !pch )
    {
        return( NULL );
    }

    //
    //  write string for IP
    //

    pchend = Dns_Ip4AddressToReverseName_A( pch, IpAddress );
    if ( !pchend )
    {
        FREE_HEAP( pch );
        return( NULL );
    }

    return( pch );
}



PWCHAR
Dns_Ip4AddressToReverseNameAlloc_W(
    IN      IP_ADDRESS      IpAddress
    )
/*++

Routine Description:

    Create reverse lookup name string, given corresponding IP.

    Caller must free the string.

Arguments:

    IpAddress -- IP address to create

Return Value:

    Ptr to new reverse lookup string.

--*/
{
    PWCHAR   pch;
    PWCHAR   pchend;

    DNSDBG( TRACE, ( "Dns_Ip4AddressToReverseNameAlloc_W()\n" ));

    //
    //  allocate space for string
    //

    pch = ALLOCATE_HEAP( DNS_MAX_REVERSE_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
    if ( !pch )
    {
        return( NULL );
    }

    //
    //  write string for IP
    //

    pchend = Dns_Ip4AddressToReverseName_W( pch, IpAddress );
    if ( !pchend )
    {
        FREE_HEAP( pch );
        return( NULL );
    }

    return( pch );
}



//
//  Reverse lookup address-to-name -- IP6
//

PCHAR
Dns_Ip6AddressToReverseName_A(
    OUT     PCHAR           pBuffer,
    IN      IP6_ADDRESS     Ip6Addr
    )
/*++

Routine Description:

    Write reverse lookup name, given corresponding IP6 address

Arguments:

    pBuffer -- ptr to buffer for reverse lookup name;
        MUST contain at least DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH bytes

    Ip6Addr -- IP6 address to create reverse string for

Return Value:

    Ptr to next location in buffer.

--*/
{
    DWORD   i;

    DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseName_A()\n" ));

    //
    //  write digit for each nibble in IP6 address
    //
    //  note we are reversing net order here
    //      since address is in net order and we are filling
    //      in least to most significant order
    //      - go DOWN through DWORDS
    //      - go DOWN through the BYTES
    //      - but we must put the lowest (least significant) nibble
    //          first as our bits are not in "bit net order"
    //          which is sending the highest bit in the byte first
    //

#if 0
    i = 4;

    while ( i-- )
    {
        DWORD thisDword = Ip6Address.IP6Dword[i];

        pBuffer += sprintf(
                        pBuffer,
                        "%u.%u.%u.%u.%u.%u.%u.%u.",
                        (thisDword & 0x0f000000) >> 24,
                        (thisDword & 0xf0000000) >> 28,
                        (thisDword & 0x000f0000) >> 16,
                        (thisDword & 0x00f00000) >> 20,
                        (thisDword & 0x00000f00) >>  8,
                        (thisDword & 0x0000f000) >> 12,
                        (thisDword & 0x0000000f)      ,
                        (thisDword & 0x000000f0) >>  4
                        );
    }
#endif
    i = 16;

    while ( i-- )
    {
        BYTE thisByte = Ip6Addr.IP6Byte[i];

        pBuffer += sprintf(
                        pBuffer,
                        "%x.%x.",
                        (thisByte & 0x0f),
                        (thisByte & 0xf0) >> 4
                        );
    }

    pBuffer += sprintf(
                    pBuffer,
                    "ip6.int." );

    return( pBuffer );
}



PWCHAR
Dns_Ip6AddressToReverseName_W(
    OUT     PWCHAR          pBuffer,
    IN      IP6_ADDRESS     Ip6Addr
    )
/*++

Routine Description:

    Write reverse lookup name, given corresponding IP6 address

Arguments:

    pBuffer -- ptr to buffer for reverse lookup name;
        MUST contain at least DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH wide chars

    Ip6Addr -- IP6 address to create reverse string for

Return Value:

    Ptr to next location in buffer.

--*/
{
    DWORD   i;

    DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseName_W()\n" ));

    //
    //  write digit for each nibble in IP6 address
    //      - in net order so lowest nibble is in highest memory
    //

    i = 16;

    while ( i-- )
    {
        BYTE thisByte = Ip6Addr.IP6Byte[i];

        pBuffer += wsprintfW(
                        pBuffer,
                        L"%x.%x.",
                        (thisByte & 0x0f),
                        (thisByte & 0xf0) >> 4
                        );
    }

    pBuffer += wsprintfW(
                    pBuffer,
                    L"ip6.int." );

    return( pBuffer );
}



PCHAR
Dns_Ip6AddressToReverseNameAlloc_A(
    IN      IP6_ADDRESS     Ip6Addr
    )
/*++

Routine Description:

    Create reverse lookup name given corresponding IP.

    Caller must free the string.

Arguments:

    Ip6Addr -- IP6 address to create reverse name for

Return Value:

    Ptr to new reverse lookup name string.

--*/
{
    PCHAR   pch;
    PCHAR   pchend;

    DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseNameAlloc_A()\n" ));

    //
    //  allocate space for string
    //

    pch = ALLOCATE_HEAP( DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH );
    if ( !pch )
    {
        return( NULL );
    }

    //
    //  write string for IP
    //

    pchend = Dns_Ip6AddressToReverseName_A( pch, Ip6Addr );
    if ( !pchend )
    {
        FREE_HEAP( pch );
        return( NULL );
    }

    return( pch );
}



PWCHAR
Dns_Ip6AddressToReverseNameAlloc_W(
    IN      IP6_ADDRESS     Ip6Addr
    )
/*++

Routine Description:

    Create reverse lookup name given corresponding IP.

    Caller must free the string.

Arguments:

    Ip6Addr -- IP6 address to create reverse name for

Return Value:

    Ptr to new reverse lookup name string.

--*/
{
    PWCHAR  pch;
    PWCHAR  pchend;

    DNSDBG( TRACE, ( "Dns_Ip6AddressToReverseNameAlloc_W()\n" ));

    //
    //  allocate space for string
    //

    pch = (PWCHAR) ALLOCATE_HEAP(
                    DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
    if ( !pch )
    {
        return( NULL );
    }

    //
    //  write string for IP
    //

    pchend = Dns_Ip6AddressToReverseName_W( pch, Ip6Addr );
    if ( !pchend )
    {
        FREE_HEAP( pch );
        return( NULL );
    }

    return( pch );
}



//
//  Reverse name-to-address -- IP4
//

BOOL
Dns_Ip4ReverseNameToAddress_A(
    OUT     PIP4_ADDRESS    pIp4Addr,
    IN      PCSTR           pszName
    )
/*++

Routine Description:

    Get IP for reverse lookup name.

Arguments:

    pIp4Addr -- addr to receive IP address if found

    pszName -- name to lookup

Return Value:

    TRUE -- if reverse lookup name converted to IP
    FALSE -- if not IP4 reverse lookup name

--*/
{
#define SIZE_IP4REV  (sizeof(".in-addr.arpa")-1)

    CHAR        nameBuffer[ DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH+1 ];
    DWORD       nameLength;
    IP_ADDRESS  ip;
    PCHAR       pch;
    DWORD       i;
    DWORD       byte;

    DNSDBG( TRACE, (
        "Dns_Ip4ReverseNameToAddress_A( %s )\n",
        pszName ));

    //
    //  validate name
    //  fail if
    //      - too long
    //      - too short
    //      - not in in-addr.arpa domain
    //

    nameLength = strlen( pszName );

    if ( nameLength > DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH )
    {
        return( FALSE );
    }
    if ( pszName[nameLength-1] == '.' )
    {
        nameLength--;
    }
    if ( nameLength <= SIZE_IP4REV )
    {
        return( FALSE );
    }
    nameLength -= SIZE_IP4REV;

    if ( _strnicmp( ".in-addr.arpa", &pszName[nameLength], SIZE_IP4REV ) != 0 )
    {
        return( FALSE );
    }

    //
    //  copy reverse dotted decimal piece of name
    //

    RtlCopyMemory(
        nameBuffer,
        pszName,
        nameLength );

    nameBuffer[nameLength] = 0;

    //
    //  read digits
    //

    ip = 0;
    i = 0;

    pch = nameBuffer + nameLength;

    while ( 1 )
    {
        --pch;

        if ( *pch == '.' )
        {
            *pch = 0;
            pch++;
        }
        else if ( pch == nameBuffer )
        {
        }
        else
        {
            continue;
        }

        //  convert byte

        byte = strtoul( pch, NULL, 10 );
        if ( byte > 255 )
        {
            return( FALSE );
        }
        if ( i > 3 )
        {
            return( FALSE );
        }
        ip |= byte << (8*i);

        //  terminate at string beginning
        //  or continue back up string

        if ( pch == nameBuffer )
        {
            break;
        }
        i++;
        pch--;
    }

    *pIp4Addr = ip;

    DNSDBG( TRACE, (
        "Success on Dns_Ip4ReverseNameToAddress_A( %s ) => %s\n",
        pszName,
        IP_STRING(ip) ));

    return( TRUE );
}



BOOL
Dns_Ip4ReverseNameToAddress_W(
    OUT     PIP4_ADDRESS    pIp4Addr,
    IN      PCWSTR          pwsName
    )
/*++

Routine Description:

    Get IP for reverse lookup name.

Arguments:

    pIp4Addr -- addr to receive IP address if found

    pszName -- name to lookup

Return Value:

    TRUE -- if reverse lookup name converted to IP
    FALSE -- if not IP4 reverse lookup name

--*/
{
    CHAR        nameBuffer[ DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH+1 ];
    DWORD       bufLength;
    DWORD       nameLengthUtf8;


    DNSDBG( TRACE, (
        "Dns_Ip4ReverseNameToAddress_W( %S )\n",
        pwsName ));

    //
    //  convert to UTF8
    //      - use UTF8 since conversion to it is trivial and it
    //      is identical to ANSI for all reverse lookup names
    //

    bufLength = DNS_MAX_IP4_REVERSE_NAME_BUFFER_LENGTH + 1;

    nameLengthUtf8 = Dns_StringCopy(
                        nameBuffer,
                        & bufLength,
                        (PCHAR) pwsName,
                        0,          // NULL terminated
                        DnsCharSetUnicode,
                        DnsCharSetUtf8 );
    if ( nameLengthUtf8 == 0 )
    {
        return  FALSE;
    }

    //
    //  call ANSI routine to do conversion
    //

    return  Dns_Ip4ReverseNameToAddress_A(
                pIp4Addr,
                (PCSTR) nameBuffer );
}



//
//  Reverse name-to-address -- IP6
//

BOOL
Dns_Ip6ReverseNameToAddress_A(
    OUT     PIP6_ADDRESS    pIp6Addr,
    IN      PCSTR           pszName
    )
/*++

Routine Description:

    Get IP6 address for reverse lookup name.

Arguments:

    pIp6Addr -- addr to receive IP address if found

    pszName -- name to lookup

Return Value:

    TRUE -- if reverse lookup name converted to IP
    FALSE -- if not IP4 reverse lookup name

--*/
{
#define SIZE_IP6REV  (sizeof(".ip6.int")-1)

    CHAR            nameBuffer[ DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH+1 ];
    DWORD           nameLength;
    PCHAR           pch;
    BYTE            byteArray[16];
    DWORD           byteCount;
    DWORD           nibble;
    DWORD           highNibble;
    BOOL            fisLow;

    DNSDBG( TRACE, ( "Dns_Ip6ReverseNameToAddress_A()\n" ));

    //
    //  validate name
    //  fail if
    //      - too long
    //      - too short
    //      - not in in6.int domain
    //

    nameLength = strlen( pszName );

    if ( nameLength > DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH )
    {
        return( FALSE );
    }
    if ( pszName[nameLength-1] == '.' )
    {
        nameLength--;
    }
    if ( nameLength <= SIZE_IP6REV )
    {
        return( FALSE );
    }
    nameLength -= SIZE_IP6REV;

    if ( _strnicmp( ".ip6.int", &pszName[nameLength], SIZE_IP6REV ) != 0 )
    {
        return( FALSE );
    }

    //
    //  copy name
    //

    RtlCopyMemory(
        nameBuffer,
        pszName,
        nameLength );

    nameBuffer[nameLength] = 0;

    //
    //  clear IP6 address
    //      - need for partial reverse lookup name
    //

    RtlZeroMemory(
        byteArray,
        sizeof(byteArray) );

    //
    //  read digits
    //

    byteCount = 0;
    fisLow = FALSE;

    pch = nameBuffer + nameLength;

    while ( 1 )
    {
        if ( byteCount > 15 )
        {
            return( FALSE );
        }

        --pch;

        if ( *pch == '.' )
        {
            *pch = 0;
            pch++;
        }
        else if ( pch == nameBuffer )
        {
        }
        else
        {
            //  DCR:   multi-digit nibbles in reverse name -- error?
            continue;
        }

        //  convert nibble
        //      - zero test special as
        //      A) faster
        //      B) strtoul() uses for error case

        if ( *pch == '0' )
        {
            nibble = 0;
        }
        else
        {
            nibble = strtoul( pch, NULL, 16 );
            if ( nibble == 0 || nibble > 15 )
            {
                return( FALSE );
            }
        }

        //  save high nibble
        //  on low nibble, write byte to IP6 address

        if ( !fisLow )
        {
            highNibble = nibble;
            fisLow = TRUE;
        }
        else
        {
            //byteArray[byteCount++] = (BYTE) (lowNibble | (nibble << 4));

            pIp6Addr->IP6Byte[byteCount++] = (BYTE) ( (highNibble<<4) | nibble );
            fisLow = FALSE;
        }

        //  terminate at string beginning
        //  or continue back up string

        if ( pch == nameBuffer )
        {
            break;
        }
        pch--;
    }

    //*pIp6Addr = *(PIP6_ADDRESS) byteArray;

    DNSDBG( TRACE, (
        "Success on Dns_Ip6ReverseNameToAddress_A( %s )\n",
        pszName ));

    return( TRUE );
}



BOOL
Dns_Ip6ReverseNameToAddress_W(
    OUT     PIP6_ADDRESS    pIp6Addr,
    IN      PCWSTR          pwsName
    )
/*++

Routine Description:

    Get IP for reverse lookup name.

Arguments:

    pIp6Addr -- addr to receive IP address if found

    pszName -- name to lookup

Return Value:

    TRUE -- if reverse lookup name converted to IP
    FALSE -- if not IP6 reverse lookup name

--*/
{
    CHAR        nameBuffer[ DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH+1 ];
    DWORD       bufLength;
    DWORD       nameLengthUtf8;


    DNSDBG( TRACE, (
        "Dns_Ip6ReverseNameToAddress_W( %S )\n",
        pwsName ));

    //
    //  convert to UTF8
    //      - use UTF8 since conversion to it is trivial and it
    //      is identical to ANSI for all reverse lookup names
    //

    bufLength = DNS_MAX_IP6_REVERSE_NAME_BUFFER_LENGTH + 1;

    nameLengthUtf8 = Dns_StringCopy(
                        nameBuffer,
                        & bufLength,
                        (PCHAR) pwsName,
                        0,          // NULL terminated
                        DnsCharSetUnicode,
                        DnsCharSetUtf8 );
    if ( nameLengthUtf8 == 0 )
    {
        return  FALSE;
    }

    //
    //  call ANSI routine to do conversion
    //

    return  Dns_Ip6ReverseNameToAddress_A(
                pIp6Addr,
                (PCSTR) nameBuffer );
}



//
//  Combined IP4/IP6 reverse-name-to-address
//

BOOL
Dns_ReverseNameToAddress_W(
    OUT     PCHAR           pAddrBuf,
    IN OUT  PDWORD          pBufLength,
    IN      PCWSTR          pString,
    IN OUT  PDWORD          pAddrFamily
    )
/*++

Routine Description:

    Build address (IP4 or IP6) from reverse lookup name.

Arguments:

    pAddrBuf -- buffer to receive address

    pBufLength -- ptr to address length
        input   - length of buffer
        output  - length of address found

    pString -- address string

    pAddrFamily -- ptr to address family
        input   - zero for any family or particular family to check
        output  - family found;  zero if no conversion

Return Value:

    TRUE if successful.
    FALSE on error.  GetLastError() for status.

--*/
{
    return  Dns_StringToAddressEx(
                pAddrBuf,
                pBufLength,
                (PCSTR) pString,
                pAddrFamily,
                TRUE,       // unicode
                TRUE        // reverse
                );
}

BOOL
Dns_ReverseNameToAddress_A(
    OUT     PCHAR           pAddrBuf,
    IN OUT  PDWORD          pBufLength,
    IN      PCSTR           pString,
    IN OUT  PDWORD          pAddrFamily
    )
{
    return  Dns_StringToAddressEx(
                pAddrBuf,
                pBufLength,
                pString,
                pAddrFamily,
                FALSE,      // ANSI
                TRUE        // reverse
                );
}




//
//  Combined string-to-address private workhorse
//

BOOL
Dns_StringToAddressEx(
    OUT     PCHAR           pAddrBuf,
    IN OUT  PDWORD          pBufLength,
    IN      PCSTR           pString,
    IN OUT  PDWORD          pAddrFamily,
    IN      BOOL            fUnicode,
    IN      BOOL            fReverse
    )
/*++

Routine Description:

    Build address (IP4 or IP6 from string)

    This routine is capable of all string-to-address
    conversions and is the backbone of all the
    combined string-to-address conversion routines.

Arguments:

    pAddrBuf -- buffer to receive address

    pBufLength -- ptr to address length
        input   - length of buffer
        output  - length of address found

    pString -- address string

    pAddrFamily -- ptr to address family
        input   - zero for any family or particular family to check
        output  - family found;  zero if no conversion

    fUnicode -- unicode string

    fReverse -- reverse lookup string

Return Value:

    TRUE if successful.
    FALSE on error.

--*/
{
    DNS_STATUS  status = NO_ERROR;
    DWORD       length = 0;
    INT         family = *pAddrFamily;
    DWORD       bufLength = *pBufLength;
    BOOL        fconvert;
    PCSTR       preverseString;
    CHAR        nameBuffer[ DNS_MAX_REVERSE_NAME_BUFFER_LENGTH+1 ];

    DNSDBG( TRACE, (
        "Dns_StringToAddressEx( %S%s )\n",
        fUnicode ? pString : "",
        fUnicode ? "" : pString ));

    //
    //  convert reverse to ANSI
    //  
    //  reverse lookups are done in ANSI;  convert here to avoid
    //  double string conversion to check both IP4 and IP6
    //

    if ( fReverse )
    {
        preverseString = pString;

        if ( fUnicode )
        {
            DWORD   bufLength = DNS_MAX_REVERSE_NAME_BUFFER_LENGTH;

            if ( ! Dns_StringCopy(
                        nameBuffer,
                        & bufLength,
                        (PCHAR) pString,
                        0,          // NULL terminated
                        DnsCharSetUnicode,
                        DnsCharSetUtf8 ) )
            {
                return  FALSE;
            }
            preverseString = nameBuffer;
        }
    }

    //
    //  check IP4
    //

    if ( family == 0 ||
         family == AF_INET )
    {
        IP4_ADDRESS ip;

        if ( fReverse )
        {
            fconvert = Dns_Ip4ReverseNameToAddress_A(
                            & ip,
                            preverseString );
        }
        else
        {
            if ( fUnicode )
            {
                fconvert = Dns_Ip4StringToAddress_W(
                                & ip,
                                (PCWSTR)pString );
            }
            else
            {
                fconvert = Dns_Ip4StringToAddress_A(
                                & ip,
                                pString );
            }
        }
        if ( fconvert )
        {
            length = sizeof(IP4_ADDRESS);
            family = AF_INET;

            if ( bufLength < length )
            {
                status = ERROR_MORE_DATA;
            }
            else
            {
                * (PIP4_ADDRESS) pAddrBuf = ip;
            }

            DNSDBG( INIT2, (
                "Converted string to IP4 address %s\n",
                IP_STRING(ip) ));
            goto Done;
        }
    }

    //
    //  check IP6
    //

    if ( family == 0 ||
         family == AF_INET6 )
    {
        IP6_ADDRESS ip;

        if ( fReverse )
        {
            fconvert = Dns_Ip6ReverseNameToAddress_A(
                            & ip,
                            preverseString );
        }
        else
        {
            if ( fUnicode )
            {
                fconvert = Dns_Ip6StringToAddress_W(
                                & ip,
                                (PCWSTR)pString );
            }
            else
            {
                fconvert = Dns_Ip6StringToAddress_A(
                                & ip,
                                pString );
            }
        }
        if ( fconvert )
        {
            length = sizeof(IP6_ADDRESS);

            if ( bufLength < length )
            {
                status = ERROR_MORE_DATA;
            }
            else
            {
                family = AF_INET6;
                * (PIP6_ADDRESS) pAddrBuf = ip;
            }

            IF_DNSDBG( INIT2 )
            {
                DnsDbg_Ip6Address(
                    "Converted string to IP6 address: ",
                    (PIP6_ADDRESS) pAddrBuf,
                    "\n" );
            }
            goto Done;
        }
    }

    length = 0;
    family = 0;
    status = DNS_ERROR_INVALID_IP_ADDRESS;

Done:

    if ( status )
    {
        SetLastError( status );
    }

    *pAddrFamily = family;
    *pBufLength = length;

    DNSDBG( TRACE, (
        "Leave Dns_StringToAddressEx()\n"
        "\tstatus   = %d\n"
        "\tptr      = %p\n"
        "\tlength   = %d\n"
        "\tfamily   = %d\n",
        status,
        pAddrBuf,
        length,
        family ));

    return( status==ERROR_SUCCESS );
}


//
//  End straddr.c
//