CVS: winex/dlls/dnsapi dnsapi.h, NONE, 1.1 dnsapi.spec, NONE, 1.1 main.c, NONE, 1.1 Makefile.in, NONE, 1.1 name.c, NONE, 1.1 ns_name.c, NONE, 1.1 ns_parse.c, NONE, 1.1 query.c, NONE, 1.1 record.c, NONE, 1.1

[email protected] 29 Aug 2007 11:59:03 -0000
Newsgroups gmane.comp.emulators.winex.cvs
Message-ID <[email protected]>
Subject: winex/dlls/dnsapi dnsapi.h,NONE,1.1 dnsapi.spec,NONE,1.1 main.c,NONE,1.1 Makefile.in,NONE,1.1 name.c,NONE,1.1 ns_name.c,NONE,1.1 ns_parse.c,NONE,1.1 query.c,NONE,1.1 record.c,NONE,1.1Update of /var/lib/cvsd/cvsroot/winex/dlls/dnsapi
In directory agravaine:/tmp/cvs-serv29642/dlls/dnsapi

Added Files:
	dnsapi.h dnsapi.spec main.c Makefile.in name.c ns_name.c 
	ns_parse.c query.c record.c 
Log Message:
Import dnsapi from WineHQ
TRAC #2084


--- NEW FILE: dnsapi.h ---
/*
 * DNS support
 *
 * Copyright 2006 Hans Leidekker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */


static inline void *dns_alloc( SIZE_T size )
{
    return HeapAlloc( GetProcessHeap(), 0, size );
}

static inline void *dns_zero_alloc( SIZE_T size )
{
    return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
}

static inline void dns_free( LPVOID mem )
{
    HeapFree( GetProcessHeap(), 0, mem );
}

static inline LPSTR dns_strdup_a( LPCSTR src )
{
    LPSTR dst;

    if (!src) return NULL;
    dst = dns_alloc( (lstrlenA( src ) + 1) * sizeof(char) );
    if (dst) lstrcpyA( dst, src );
    return dst;
}

static inline char *dns_strdup_u( const char *src )
{
    char *dst;

    if (!src) return NULL;
    dst = dns_alloc( (strlen( src ) + 1) * sizeof(char) );
    if (dst) strcpy( dst, src );
    return dst;
}

static inline LPWSTR dns_strdup_w( LPCWSTR src )
{
    LPWSTR dst;

    if (!src) return NULL;
    dst = dns_alloc( (lstrlenW( src ) + 1) * sizeof(WCHAR) );
    if (dst) lstrcpyW( dst, src );
    return dst;
}

static inline LPWSTR dns_strdup_aw( LPCSTR str )
{
    LPWSTR ret = NULL;
    if (str)
    {
        DWORD len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
        if ((ret = dns_alloc( len * sizeof(WCHAR) )))
            MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
    }
    return ret;
}

static inline LPWSTR dns_strdup_uw( const char *str )
{
    LPWSTR ret = NULL;
    if (str)
    {
        DWORD len = MultiByteToWideChar( CP_UTF8, 0, str, -1, NULL, 0 );
        if ((ret = dns_alloc( len * sizeof(WCHAR) )))
            MultiByteToWideChar( CP_UTF8, 0, str, -1, ret, len );
    }
    return ret;
}

static inline LPSTR dns_strdup_wa( LPCWSTR str )
{
    LPSTR ret = NULL;
    if (str)
    {
        DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL );
        if ((ret = dns_alloc( len )))
            WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
    }
    return ret;
}

static inline char *dns_strdup_wu( LPCWSTR str )
{
    LPSTR ret = NULL;
    if (str)
    {
        DWORD len = WideCharToMultiByte( CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL );
        if ((ret = dns_alloc( len )))
            WideCharToMultiByte( CP_UTF8, 0, str, -1, ret, len, NULL, NULL );
    }
    return ret;
}

static inline char *dns_strdup_au( LPCSTR src )
{
    char *dst = NULL;
    LPWSTR ret = dns_strdup_aw( src );

    if (ret)
    {
        dst = dns_strdup_wu( ret );
        dns_free( ret );
    }
    return dst;
}

static inline LPSTR dns_strdup_ua( const char *src )
{
    LPSTR dst = NULL;
    LPWSTR ret = dns_strdup_uw( src );

    if (ret)
    {
        dst = dns_strdup_wa( ret );
        dns_free( ret );
    }
    return dst;
}

const char *dns_type_to_str( unsigned short );

int dns_ns_initparse( const u_char *, int, ns_msg * );
int dns_ns_parserr( ns_msg *, ns_sect, int, ns_rr * );
int dns_ns_name_skip( const u_char **, const u_char * );
int dns_ns_name_uncompress( const u_char *, const u_char *, const u_char *, char *, size_t );

--- NEW FILE: dnsapi.spec ---
name	dnsapi
type	win32
init	DllMain

import netapi32.dll
import kernel32.dll

@ stdcall DnsAcquireContextHandle_A(long ptr ptr) DnsAcquireContextHandle_A
@ stdcall DnsAcquireContextHandle_UTF8(long ptr ptr) DnsAcquireContextHandle_UTF8
@ stdcall DnsAcquireContextHandle_W(long ptr ptr) DnsAcquireContextHandle_W
@ stub DnsAddRecordSet_A
@ stub DnsAddRecordSet_UTF8
@ stub DnsAddRecordSet_W
@ stub DnsAllocateRecord
@ stub DnsApiHeapReset
@ stub DnsAsyncRegisterHostAddrs_A
@ stub DnsAsyncRegisterHostAddrs_UTF8
@ stub DnsAsyncRegisterHostAddrs_W
@ stub DnsAsyncRegisterInit
@ stub DnsAsyncRegisterTerm
@ stub DnsCheckNameCollision_A
@ stub DnsCheckNameCollision_UTF8
@ stub DnsCheckNameCollision_W
@ stub DnsCopyStringEx
@ stub DnsCreateReverseNameStringForIpAddress
@ stub DnsCreateStandardDnsNameCopy
@ stub DnsCreateStringCopy
@ stub DnsDhcpSrvRegisterHostName_W
@ stub DnsDhcpSrvRegisterInit
@ stub DnsDhcpSrvRegisterTerm
@ stub DnsDisableAdapterDomainNameRegistration
@ stub DnsDisableBNodeResolverThread
@ stub DnsDisableDynamicRegistration
@ stub DnsDowncaseDnsNameLabel
@ stub DnsEnableAdapterDomainNameRegistration
@ stub DnsEnableBNodeResolverThread
@ stub DnsEnableDynamicRegistration
@ stdcall DnsExtractRecordsFromMessage_UTF8(ptr long ptr) DnsExtractRecordsFromMessage_UTF8
@ stdcall DnsExtractRecordsFromMessage_W(ptr long ptr) DnsExtractRecordsFromMessage_W
@ stub DnsFindAuthoritativeZone
@ stub DnsFlushResolverCache
@ stub DnsFlushResolverCacheEntry_A
@ stub DnsFlushResolverCacheEntry_UTF8
@ stub DnsFlushResolverCacheEntry_W
@ stub DnsFreeAdapterInformation
@ stub DnsFreeNetworkInformation
@ stub DnsFreeSearchInformation
@ stub DnsGetBufferLengthForStringCopy
@ stub DnsGetCacheDataTable
@ stub DnsGetDnsServerList
@ stub DnsGetDomainName
@ stub DnsGetHostName_A
@ stub DnsGetHostName_UTF8
@ stub DnsGetHostName_W
@ stub DnsGetIpAddressInfoList
@ stub DnsGetIpAddressList
@ stub DnsGetLastServerUpdateIP
@ stub DnsGetMaxNumberOfAddressesToRegister
@ stub DnsGetNetworkInformation
@ stub DnsGetPrimaryDomainName_A
@ stub DnsGetPrimaryDomainName_UTF8
@ stub DnsGetPrimaryDomainName_W
@ stub DnsGetSearchInformation
@ stub DnsIpv6AddressToString
@ stub DnsIpv6StringToAddress
@ stub DnsIsAdapterDomainNameRegistrationEnabled
@ stub DnsIsAMailboxType
@ stub DnsIsDynamicRegistrationEnabled
@ stub DnsIsStatusRcode
@ stub DnsIsStringCountValidForTextType
@ stub DnsMapRcodeToStatus
@ stub DnsModifyRecordSet_A
@ stub DnsModifyRecordSet_UTF8
@ stub DnsModifyRecordSet_W
@ stdcall DnsModifyRecordsInSet_A(ptr ptr long ptr ptr ptr) DnsModifyRecordsInSet_A
@ stdcall DnsModifyRecordsInSet_UTF8(ptr ptr long ptr ptr ptr) DnsModifyRecordsInSet_UTF8
@ stdcall DnsModifyRecordsInSet_W(ptr ptr long ptr ptr ptr) DnsModifyRecordsInSet_W
@ stdcall DnsNameCompare_A(str str) DnsNameCompare_A
@ stub DnsNameCompareEx_A
@ stub DnsNameCompareEx_UTF8
@ stub DnsNameCompareEx_W
@ stdcall DnsNameCompare_W(wstr wstr) DnsNameCompare_W
@ stub DnsNameCopy
@ stub DnsNameCopyAllocate
@ stub DnsNotifyResolver
@ stdcall DnsQuery_A(str long long ptr ptr ptr) DnsQuery_A
@ stdcall DnsQueryConfig(long long wstr ptr ptr ptr) DnsQueryConfig
@ stub DnsQueryEx
@ stdcall DnsQuery_UTF8(str long long ptr ptr ptr) DnsQuery_UTF8
@ stdcall DnsQuery_W(wstr long long ptr ptr ptr) DnsQuery_W
@ stub DnsRecordBuild_UTF8
@ stub DnsRecordBuild_W
@ stdcall DnsRecordCompare(ptr ptr) DnsRecordCompare
@ stdcall DnsRecordCopyEx(ptr long long) DnsRecordCopyEx
@ stdcall DnsRecordListFree(ptr long) DnsRecordListFree
@ stdcall DnsRecordSetCompare(ptr ptr ptr ptr) DnsRecordSetCompare
@ stdcall DnsRecordSetCopyEx(ptr long long) DnsRecordSetCopyEx
@ stdcall DnsRecordSetDetach(ptr) DnsRecordSetDetach
@ stub DnsRecordStringForType
@ stub DnsRecordStringForWritableType
@ stub DnsRecordTypeForName
@ stub DnsRelationalCompare_UTF8
@ stub DnsRelationalCompare_W
@ stdcall DnsReleaseContextHandle(ptr) DnsReleaseContextHandle
@ stub DnsRemoveRegistrations
@ stdcall DnsReplaceRecordSetA(ptr long ptr ptr ptr) DnsReplaceRecordSetA
@ stub DnsReplaceRecordSet_A
@ stdcall DnsReplaceRecordSetUTF8(ptr long ptr ptr ptr) DnsReplaceRecordSetUTF8
@ stub DnsReplaceRecordSet_UTF8
@ stdcall DnsReplaceRecordSetW(ptr long ptr ptr ptr) DnsReplaceRecordSetW
@ stub DnsReplaceRecordSet_W
@ stub DnsServiceNotificationDeregister_A
@ stub DnsServiceNotificationDeregister_UTF8
@ stub DnsServiceNotificationDeregister_W
@ stub DnsServiceNotificationRegister_A
@ stub DnsServiceNotificationRegister_UTF8
@ stub DnsServiceNotificationRegister_W
@ stub DnsSetMaxNumberOfAddressesToRegister
@ stub DnsStatusString
@ stub DnsStringCopyAllocateEx
@ stub DnsUnicodeToUtf8
@ stub DnsUpdate
@ stub DnsUpdateTest_A
@ stub DnsUpdateTest_UTF8
@ stub DnsUpdateTest_W
@ stub DnsUtf8ToUnicode
@ stdcall DnsValidateName_A(str long) DnsValidateName_A
@ stdcall DnsValidateName_UTF8(str long) DnsValidateName_UTF8
@ stdcall DnsValidateName_W(wstr long) DnsValidateName_W
@ stub DnsValidateUtf8Byte
@ stub DnsWinsRecordFlagForString
@ stub DnsWinsRecordFlagString
@ stdcall DnsWriteQuestionToBuffer_UTF8(ptr ptr str long long long) DnsWriteQuestionToBuffer_UTF8
@ stdcall DnsWriteQuestionToBuffer_W(ptr ptr wstr long long long) DnsWriteQuestionToBuffer_W
@ stub DnsWriteReverseNameStringForIpAddress
@ stub GetCurrentTimeInSeconds

--- NEW FILE: main.c ---
/*
 * DNS support
 *
 * Copyright (C) 2006 Matthew Kehrer
 * Copyright (C) 2006 Hans Leidekker
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine-lgpl/windns.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(dnsapi);

static HINSTANCE hdnsapi;

BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
{
    TRACE( "(%p, %d, %p)\n", hinst, reason, reserved );

    switch (reason)
    {
#if 0
    case DLL_WINE_PREATTACH:
        return FALSE;  /* prefer native version */
#endif
    case DLL_PROCESS_ATTACH:
        hdnsapi = hinst;
        DisableThreadLibraryCalls( hinst );
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

/******************************************************************************
 * DnsAcquireContextHandle_A              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsAcquireContextHandle_A( DWORD flags, PVOID cred,
                                             PHANDLE context )
{
    FIXME( "(0x%08x,%p,%p) stub\n", flags, cred, context );

    *context = (HANDLE)0xdeadbeef;
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsAcquireContextHandle_UTF8              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsAcquireContextHandle_UTF8( DWORD flags, PVOID cred,
                                                PHANDLE context )
{
    FIXME( "(0x%08x,%p,%p) stub\n", flags, cred, context );

    *context = (HANDLE)0xdeadbeef;
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsAcquireContextHandle_W              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsAcquireContextHandle_W( DWORD flags, PVOID cred,
                                             PHANDLE context )
{
    FIXME( "(0x%08x,%p,%p) stub\n", flags, cred, context );

    *context = (HANDLE)0xdeadbeef;
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsReleaseContextHandle                [DNSAPI.@]
 *
 */
VOID WINAPI DnsReleaseContextHandle( HANDLE context )
{
    FIXME( "(%p) stub\n", context );
}

/******************************************************************************
 * DnsExtractRecordsFromMessage_UTF8       [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsExtractRecordsFromMessage_UTF8( PDNS_MESSAGE_BUFFER buffer,
                                                     WORD len, PDNS_RECORDA *record )
{
    FIXME( "(%p,%d,%p) stub\n", buffer, len, record );

    *record = NULL;
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsExtractRecordsFromMessage_W          [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsExtractRecordsFromMessage_W( PDNS_MESSAGE_BUFFER buffer,
                                                  WORD len, PDNS_RECORDW *record )
{
    FIXME( "(%p,%d,%p) stub\n", buffer, len, record );

    *record = NULL;
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsModifyRecordsInSet_A                 [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsModifyRecordsInSet_A( PDNS_RECORDA add, PDNS_RECORDA delete,
                                           DWORD options, HANDLE context,
                                           PVOID servers, PVOID reserved )
{
    FIXME( "(%p,%p,0x%08x,%p,%p,%p) stub\n", add, delete, options,
           context, servers, reserved );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsModifyRecordsInSet_UTF8              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsModifyRecordsInSet_UTF8( PDNS_RECORDA add, PDNS_RECORDA delete,
                                              DWORD options, HANDLE context,
                                              PVOID servers, PVOID reserved )
{
    FIXME( "(%p,%p,0x%08x,%p,%p,%p) stub\n", add, delete, options,
           context, servers, reserved );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsModifyRecordsInSet_W                 [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsModifyRecordsInSet_W( PDNS_RECORDW add, PDNS_RECORDW delete,
                                           DWORD options, HANDLE context,
                                           PVOID servers, PVOID reserved )
{
    FIXME( "(%p,%p,0x%08x,%p,%p,%p) stub\n", add, delete, options,
           context, servers, reserved );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsWriteQuestionToBuffer_UTF8          [DNSAPI.@]
 *
 */
BOOL WINAPI DnsWriteQuestionToBuffer_UTF8( PDNS_MESSAGE_BUFFER buffer, PDWORD size,
                                           PCSTR name, WORD type, WORD xid,
                                           BOOL recurse )
{
    FIXME( "(%p,%p,%s,%d,%d,%d) stub\n", buffer, size, debugstr_a(name),
           type, xid, recurse );
    return FALSE;
}

/******************************************************************************
 * DnsWriteQuestionToBuffer_W              [DNSAPI.@]
 *
 */
BOOL WINAPI DnsWriteQuestionToBuffer_W( PDNS_MESSAGE_BUFFER buffer, PDWORD size,
                                        PCWSTR name, WORD type, WORD xid,
                                        BOOL recurse )
{
    FIXME( "(%p,%p,%s,%d,%d,%d) stub\n", buffer, size, debugstr_w(name),
           type, xid, recurse );
    return FALSE;
}

/******************************************************************************
 * DnsReplaceRecordSetA                    [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsReplaceRecordSetA( PDNS_RECORDA set, DWORD options,
                                        HANDLE context, PVOID servers,
                                        PVOID reserved )
{
    FIXME( "(%p,0x%08x,%p,%p,%p) stub\n", set, options, context,
           servers, reserved );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsReplaceRecordSetUTF8                 [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsReplaceRecordSetUTF8( PDNS_RECORDA set, DWORD options,
                                           HANDLE context, PVOID servers,
                                           PVOID reserved )
{
    FIXME( "(%p,0x%08x,%p,%p,%p) stub\n", set, options, context,
           servers, reserved );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsReplaceRecordSetW                    [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsReplaceRecordSetW( PDNS_RECORDW set, DWORD options,
                                        HANDLE context, PVOID servers,
                                        PVOID reserved )
{
    FIXME( "(%p,0x%08x,%p,%p,%p) stub\n", set, options, context,
           servers, reserved );
    return ERROR_SUCCESS;
}

--- NEW FILE: Makefile.in ---
EXTRADEFS = -D__WINESRC__ -DSTRICT
TOPSRCDIR = @top_srcdir@
TOPOBJDIR = ../..
SRCDIR    = @srcdir@
VPATH     = @srcdir@
MODULE    = dnsapi
EXTRALIBS = @LIBRESOLV@

LDDLLFLAGS = @LDDLLFLAGS@
SYMBOLFILE = $(MODULE).tmp.o

C_SRCS = \
	main.c \
	name.c \
	ns_name.c \
	ns_parse.c \
	query.c \
	record.c

@MAKE_DLL_RULES@

### Dependencies:

--- NEW FILE: name.c ---
/*
 * DNS support
 *
 * Copyright (C) 2006 Matthew Kehrer
 * Copyright (C) 2006 Hans Leidekker
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/debug.h"
#include "wine/unicode.h"

#include <stdarg.h>
#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
# undef NOERROR
#endif
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winnls.h"
#include "wine-lgpl/windns.h"

#include "dnsapi.h"

WINE_DEFAULT_DEBUG_CHANNEL(dnsapi);

/******************************************************************************
 * DnsNameCompare_A               [DNSAPI.@]
 *
 */
BOOL WINAPI DnsNameCompare_A( PCSTR name1, PCSTR name2 )
{
    BOOL ret;
    PWSTR name1W, name2W;

    TRACE( "(%s,%s)\n", debugstr_a(name1), debugstr_a(name2) );

    name1W = dns_strdup_aw( name1 );
    name2W = dns_strdup_aw( name2 );

    ret = DnsNameCompare_W( name1W, name2W );

    dns_free( name1W );
    dns_free( name2W );

    return ret;
}

/******************************************************************************
 * DnsNameCompare_W               [DNSAPI.@]
 *
 */
BOOL WINAPI DnsNameCompare_W( PCWSTR name1, PCWSTR name2 )
{
    PCWSTR p, q;

    TRACE( "(%s,%s)\n", debugstr_w(name1), debugstr_w(name2) );

    if (!name1 && !name2) return TRUE;
    if (!name1 || !name2) return FALSE;
 
    p = name1 + lstrlenW( name1 ) - 1;
    q = name2 + lstrlenW( name2 ) - 1;

    while (*p == '.' && p >= name1) p--;
    while (*q == '.' && q >= name2) q--;

    if (p - name1 != q - name2) return FALSE;

    while (name1 <= p)
    {
        if (toupperW( *name1 ) != toupperW( *name2 ))
            return FALSE;

        name1++;
        name2++;
    }
    return TRUE;
}

/******************************************************************************
 * DnsValidateName_A              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsValidateName_A( PCSTR name, DNS_NAME_FORMAT format )
{
    PWSTR nameW;
    DNS_STATUS ret;

    TRACE( "(%s, %d)\n", debugstr_a(name), format );

    nameW = dns_strdup_aw( name );
    ret = DnsValidateName_W( nameW, format );

    dns_free( nameW );
    return ret;
}

/******************************************************************************
 * DnsValidateName_UTF8           [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsValidateName_UTF8( PCSTR name, DNS_NAME_FORMAT format )
{
    PWSTR nameW;
    DNS_STATUS ret;

    TRACE( "(%s, %d)\n", debugstr_a(name), format );

    nameW = dns_strdup_uw( name );
    ret = DnsValidateName_W( nameW, format );

    dns_free( nameW );
    return ret;
}

#define HAS_EXTENDED        0x0001
#define HAS_NUMERIC         0x0002
#define HAS_NON_NUMERIC     0x0004
#define HAS_DOT             0x0008
#define HAS_DOT_DOT         0x0010
#define HAS_SPACE           0x0020
#define HAS_INVALID         0x0040
#define HAS_ASTERISK        0x0080
#define HAS_UNDERSCORE      0x0100
#define HAS_LONG_LABEL      0x0200

/******************************************************************************
 * DnsValidateName_W              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsValidateName_W( PCWSTR name, DNS_NAME_FORMAT format )
{
    PCWSTR p;
    unsigned int i, j, state = 0;
    static const WCHAR invalid[] = {
        '{','|','}','~','[','\\',']','^','\'',':',';','<','=','>',
        '?','@','!','\"','#','$','%','^','`','(',')','+','/',',',0 };

    TRACE( "(%s, %d)\n", debugstr_w(name), format );

    if (!name) return ERROR_INVALID_NAME;

    for (p = name, i = 0, j = 0; *p; p++, i++, j++)
    {
        if (*p == '.')
        {
            j = 0;
            state |= HAS_DOT;
            if (p[1] == '.') state |= HAS_DOT_DOT;
        }
        else if (*p < '0' || *p > '9') state |= HAS_NON_NUMERIC;
        else state |= HAS_NUMERIC;

        if (j > 62) state |= HAS_LONG_LABEL;

        if (strchrW( invalid, *p )) state |= HAS_INVALID;
        else if ((unsigned)*p > 127) state |= HAS_EXTENDED;
        else if (*p == ' ') state |= HAS_SPACE;
        else if (*p == '_') state |= HAS_UNDERSCORE;
        else if (*p == '*') state |= HAS_ASTERISK;
    }

    if (i == 0 || i > 255 ||
        (state & HAS_LONG_LABEL) ||
        (state & HAS_DOT_DOT) ||
        (name[0] == '.' && name[1])) return ERROR_INVALID_NAME;

    switch (format)
    {
    case DnsNameDomain:
    {
        if (!(state & HAS_NON_NUMERIC) && (state & HAS_NUMERIC))
            return DNS_ERROR_NUMERIC_NAME;
        if ((state & HAS_EXTENDED) || (state & HAS_UNDERSCORE))
            return DNS_ERROR_NON_RFC_NAME;
        if ((state & HAS_SPACE) ||
            (state & HAS_INVALID) ||
            (state & HAS_ASTERISK)) return DNS_ERROR_INVALID_NAME_CHAR;
        break;
    }
    case DnsNameDomainLabel:
    {
        if (state & HAS_DOT) return ERROR_INVALID_NAME;
        if ((state & HAS_EXTENDED) || (state & HAS_UNDERSCORE))
            return DNS_ERROR_NON_RFC_NAME;
        if ((state & HAS_SPACE) ||
            (state & HAS_INVALID) ||
            (state & HAS_ASTERISK)) return DNS_ERROR_INVALID_NAME_CHAR;
        break;
    }
    case DnsNameHostnameFull:
    {
        if (!(state & HAS_NON_NUMERIC) && (state & HAS_NUMERIC))
            return DNS_ERROR_NUMERIC_NAME;
        if ((state & HAS_EXTENDED) || (state & HAS_UNDERSCORE))
            return DNS_ERROR_NON_RFC_NAME;
        if ((state & HAS_SPACE) ||
            (state & HAS_INVALID) ||
            (state & HAS_ASTERISK)) return DNS_ERROR_INVALID_NAME_CHAR;
        break;
    }
    case DnsNameHostnameLabel:
    {
        if (state & HAS_DOT) return ERROR_INVALID_NAME;
        if (!(state & HAS_NON_NUMERIC) && (state & HAS_NUMERIC))
            return DNS_ERROR_NUMERIC_NAME;
        if ((state & HAS_EXTENDED) || (state & HAS_UNDERSCORE))
            return DNS_ERROR_NON_RFC_NAME;
        if ((state & HAS_SPACE) ||
            (state & HAS_INVALID) ||
            (state & HAS_ASTERISK)) return DNS_ERROR_INVALID_NAME_CHAR;
        break;
    }
    case DnsNameWildcard:
    {
        if (!(state & HAS_NON_NUMERIC) && (state & HAS_NUMERIC))
            return ERROR_INVALID_NAME;
        if (name[0] != '*') return ERROR_INVALID_NAME;
        if (name[1] && name[1] != '.')
            return DNS_ERROR_INVALID_NAME_CHAR;
        if ((state & HAS_EXTENDED) ||
            (state & HAS_SPACE) ||
            (state & HAS_INVALID)) return ERROR_INVALID_NAME;
        break;
    }
    case DnsNameSrvRecord:
    {
        if (!(state & HAS_NON_NUMERIC) && (state & HAS_NUMERIC))
            return ERROR_INVALID_NAME;
        if (name[0] != '_') return ERROR_INVALID_NAME;
        if ((state & HAS_UNDERSCORE) && !name[1])
            return DNS_ERROR_NON_RFC_NAME;
        if ((state & HAS_EXTENDED) ||
            (state & HAS_SPACE) ||
            (state & HAS_INVALID)) return ERROR_INVALID_NAME;
        break;
    }
    default:
        WARN( "unknown format: %d\n", format );
        break;
    }
    return ERROR_SUCCESS;
}

--- NEW FILE: ns_name.c ---
/*
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#include "config.h"

#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif

#include <ctype.h>
#include <errno.h>
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif
#include <string.h>
#include <ctype.h>

/* Data. */

static const char	digits[] = "0123456789";

/* Forward. */

static int		special(int);
static int		printable(int);

/* Public. */

/*
 * dns_ns_name_ntop(src, dst, dstsiz)
 *	Convert an encoded domain name to printable ascii as per RFC1035.
 * return:
 *	Number of bytes written to buffer, or -1 (with errno set)
 * notes:
 *	The root is returned as "."
 *	All other domains are returned in non absolute form
 */
static int
dns_ns_name_ntop(const u_char *src, char *dst, size_t dstsiz) {
	const u_char *cp;
	char *dn, *eom;
	u_char c;
	u_int n;

	cp = src;
	dn = dst;
	eom = dst + dstsiz;

	while ((n = *cp++) != 0) {
		if ((n & NS_CMPRSFLGS) != 0 && n != 0x41) {
			/* Some kind of compression pointer. */
			return (-1);
		}
		if (dn != dst) {
			if (dn >= eom) {
				return (-1);
			}
			*dn++ = '.';
		}

		if (n == 0x41) {
			n = *cp++ / 8;
			if (dn + n * 2 + 4 >= eom) {
				return (-1);
			}
			*dn++ = '\\';
			*dn++ = '[';
			*dn++ = 'x';

			while (n-- > 0) {
                unsigned u;
				c = *cp++;
				u = c >> 4;
				*dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
				u = c & 0xf;
				*dn++ = u > 9 ? 'a' + u - 10 : '0' + u;
			}

			*dn++ = ']';
			continue;
		}

		if (dn + n >= eom) {
			return (-1);
		}
		for ((void)NULL; n > 0; n--) {
			c = *cp++;
			if (special(c)) {
				if (dn + 1 >= eom) {
					return (-1);
				}
				*dn++ = '\\';
				*dn++ = (char)c;
			} else if (!printable(c)) {
				if (dn + 3 >= eom) {
					return (-1);
				}
				*dn++ = '\\';
				*dn++ = digits[c / 100];
				*dn++ = digits[(c % 100) / 10];
				*dn++ = digits[c % 10];
			} else {
				if (dn >= eom) {
					return (-1);
				}
				*dn++ = (char)c;
			}
		}
	}
	if (dn == dst) {
		if (dn >= eom) {
			return (-1);
		}
		*dn++ = '.';
	}
	if (dn >= eom) {
		return (-1);
	}
	*dn++ = '\0';
	return (dn - dst);
}

/*
 * dns_ns_name_pton(src, dst, dstsiz)
 *	Convert a ascii string into an encoded domain name as per RFC1035.
 * return:
 *	-1 if it fails
 *	1 if string was fully qualified
 *	0 is string was not fully qualified
 * notes:
 *	Enforces label and domain length limits.
 */

int
dns_ns_name_pton(const char *src, u_char *dst, size_t dstsiz) {
	u_char *label, *bp, *eom;
	int c, n, escaped;
	char *cp;

	escaped = 0;
	bp = dst;
	eom = dst + dstsiz;
	label = bp++;

	while ((c = *src++) != 0) {
		if (escaped) {
			if ((cp = strchr(digits, c)) != NULL) {
				n = (cp - digits) * 100;
				if ((c = *src++) == 0 ||
				    (cp = strchr(digits, c)) == NULL) {
					return (-1);
				}
				n += (cp - digits) * 10;
				if ((c = *src++) == 0 ||
				    (cp = strchr(digits, c)) == NULL) {
					return (-1);
				}
				n += (cp - digits);
				if (n > 255) {
					return (-1);
				}
				c = n;
			} else if (c == '[' && label == bp - 1 && *src == 'x') {
				/* Theoretically we would have to handle \[o
				   as well but we do not since we do not need
				   it internally.  */
				*label = 0x41;
				label = bp++;
				++src;
				while (isxdigit (*src)) {
					n = *src > '9' ? *src - 'a' + 10 : *src - '0';
					++src;
					if (! isxdigit(*src)) {
						return (-1);
					}
					n <<= 4;
					n += *src > '9' ? *src - 'a' + 10 : *src - '0';
					if (bp + 1 >= eom) {
						return (-1);
					}
					*bp++ = n;
					++src;
				}
				*label = (bp - label - 1) * 8;
				if (*src++ != ']' || *src++ != '.') {
					return (-1);
				}
				escaped = 0;
				label = bp++;
				if (bp >= eom) {
					return (-1);
				}
				continue;
			}
			escaped = 0;
		} else if (c == '\\') {
			escaped = 1;
			continue;
		} else if (c == '.') {
			c = (bp - label - 1);
			if ((c & NS_CMPRSFLGS) != 0) {	/* Label too big. */
				return (-1);
			}
			if (label >= eom) {
				return (-1);
			}
			*label = c;
			/* Fully qualified ? */
			if (*src == '\0') {
				if (c != 0) {
					if (bp >= eom) {
						return (-1);
					}
					*bp++ = '\0';
				}
				if ((bp - dst) > NS_MAXCDNAME) {
					return (-1);
				}
				return (1);
			}
			if (c == 0 || *src == '.') {
				return (-1);
			}
			label = bp++;
			continue;
		}
		if (bp >= eom) {
			return (-1);
		}
		*bp++ = (u_char)c;
	}
	c = (bp - label - 1);
	if ((c & NS_CMPRSFLGS) != 0) {		/* Label too big. */
		return (-1);
	}
	if (label >= eom) {
		return (-1);
	}
	*label = c;
	if (c != 0) {
		if (bp >= eom) {
			return (-1);
		}
		*bp++ = 0;
	}
	if ((bp - dst) > NS_MAXCDNAME) {	/* src too big */
		return (-1);
	}
	return (0);
}


/*
 * dns_ns_name_unpack(msg, eom, src, dst, dstsiz)
 *	Unpack a domain name from a message, source may be compressed.
 * return:
 *	-1 if it fails, or consumed octets if it succeeds.
 */
static int
dns_ns_name_unpack(const u_char *msg, const u_char *eom, const u_char *src,
	       u_char *dst, size_t dstsiz)
{
	const u_char *srcp, *dstlim;
	u_char *dstp;
	int n, len, checked;

	len = -1;
	checked = 0;
	dstp = dst;
	srcp = src;
	dstlim = dst + dstsiz;
	if (srcp < msg || srcp >= eom) {
		return (-1);
	}
	/* Fetch next label in domain name. */
	while ((n = *srcp++) != 0) {
		/* Check for indirection. */
		switch (n & NS_CMPRSFLGS) {
		case 0x40:
			if (n == 0x41) {
				if (dstp + 1 >= dstlim) {
					return (-1);
			  	}
				*dstp++ = 0x41;
				n = *srcp++ / 8;
				++checked;
			} else {
				return (-1);		/* flag error */
			}
			/* FALLTHROUGH */
		case 0:
			/* Limit checks. */
			if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
				return (-1);
			}
			checked += n + 1;
			dstp = memcpy(dstp, srcp - 1, n + 1);
			dstp += n + 1;
			srcp += n;
			break;

		case NS_CMPRSFLGS:
			if (srcp >= eom) {
				return (-1);
			}
			if (len < 0)
				len = srcp - src + 1;
			srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
			if (srcp < msg || srcp >= eom) {  /* Out of range. */
				return (-1);
			}
			checked += 2;
			/*
			 * Check for loops in the compressed name;
			 * if we've looked at the whole message,
			 * there must be a loop.
			 */
			if (checked >= eom - msg) {
				return (-1);
			}
			break;

		default:
			return (-1);			/* flag error */
		}
	}
	*dstp = '\0';
	if (len < 0)
		len = srcp - src;
	return (len);
}


/*
 * dns_ns_name_uncompress(msg, eom, src, dst, dstsiz)
 *	Expand compressed domain name to presentation format.
 * return:
 *	Number of bytes read out of `src', or -1 (with errno set).
 * note:
 *	Root domain returns as "." not "".
 */
int
dns_ns_name_uncompress(const u_char *msg, const u_char *eom, const u_char *src,
		   char *dst, size_t dstsiz)
{
	u_char tmp[NS_MAXCDNAME];
	int n;

	if ((n = dns_ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
		return (-1);
	if (dns_ns_name_ntop(tmp, dst, dstsiz) == -1)
		return (-1);
	return (n);
}


/*
 * dns_ns_name_skip(ptrptr, eom)
 *	Advance *ptrptr to skip over the compressed name it points at.
 * return:
 *	0 on success, -1 (with errno set) on failure.
 */
int
dns_ns_name_skip(const u_char **ptrptr, const u_char *eom) {
	const u_char *cp;
	u_int n;

	cp = *ptrptr;
	while (cp < eom && (n = *cp++) != 0) {
		/* Check for indirection. */
		switch (n & NS_CMPRSFLGS) {
		case 0:			/* normal case, n == len */
			cp += n;
			continue;
		case NS_CMPRSFLGS:	/* indirection */
			cp++;
			break;
		default:		/* illegal type */
			return (-1);
		}
		break;
	}
	if (cp > eom) {
		return (-1);
	}
	*ptrptr = cp;
	return (0);
}

/* Private. */

/*
 * special(ch)
 *	Thinking in noninternationalized USASCII (per the DNS spec),
 *	is this characted special ("in need of quoting") ?
 * return:
 *	boolean.
 */
static int
special(int ch) {
	switch (ch) {
	case 0x22: /* '"' */
	case 0x2E: /* '.' */
	case 0x3B: /* ';' */
	case 0x5C: /* '\\' */
	/* Special modifiers in zone files. */
	case 0x40: /* '@' */
	case 0x24: /* '$' */
		return (1);
	default:
		return (0);
	}
}

/*
 * printable(ch)
 *	Thinking in noninternationalized USASCII (per the DNS spec),
 *	is this character visible and not a space when printed ?
 * return:
 *	boolean.
 */
static int
printable(int ch) {
	return (ch > 0x20 && ch < 0x7f);
}

--- NEW FILE: ns_parse.c ---
/*
 * Copyright (c) 1996,1999 by Internet Software Consortium.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#include "config.h"

#ifdef HAVE_RESOLV

#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif

#include <errno.h>
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif
#include <string.h>

#include "windef.h"

/* Forward. */

static void	setsection(ns_msg *msg, ns_sect sect);

/* Macros. */

#define RETERR(err) do { return (-1); } while (0)

#ifdef HAVE_NS_MSG__MSG_PTR
# define NS_PTR(ns_msg) ((ns_msg)->_msg_ptr)
#else
# define NS_PTR(ns_msg) ((ns_msg)->_ptr)
#endif

#define DNS_NS_GET16(s, cp) do { \
    register const u_char *t_cp = (const u_char *)(cp); \
    (s) = ((WORD)t_cp[0] << 8) \
        | ((WORD)t_cp[1]) \
        ; \
    (cp) += NS_INT16SZ; \
} while (0)

#define DNS_NS_GET32(l, cp) do { \
    register const u_char *t_cp = (const u_char *)(cp); \
    (l) = ((DWORD)t_cp[0] << 24) \
        | ((DWORD)t_cp[1] << 16) \
        | ((DWORD)t_cp[2] << 8) \
        | ((DWORD)t_cp[3]) \
        ; \
    (cp) += NS_INT32SZ; \
} while (0)

/* Public. */

static int
dns_ns_skiprr(const u_char *ptr, const u_char *eom, ns_sect section, int count) {
	const u_char *optr = ptr;

	for ((void)NULL; count > 0; count--) {
		int b, rdlength;

		b = dn_skipname(ptr, eom);
		if (b < 0)
			RETERR(EMSGSIZE);
		ptr += b/*Name*/ + NS_INT16SZ/*Type*/ + NS_INT16SZ/*Class*/;
		if (section != ns_s_qd) {
			if (ptr + NS_INT32SZ + NS_INT16SZ > eom)
				RETERR(EMSGSIZE);
			ptr += NS_INT32SZ/*TTL*/;
			DNS_NS_GET16(rdlength, ptr);
			ptr += rdlength/*RData*/;
		}
	}
	if (ptr > eom)
		RETERR(EMSGSIZE);
	return (ptr - optr);
}

int
dns_ns_initparse(const u_char *msg, int msglen, ns_msg *handle) {
	const u_char *eom = msg + msglen;
	int i;

	memset(handle, 0x5e, sizeof *handle);
	handle->_msg = msg;
	handle->_eom = eom;
	if (msg + NS_INT16SZ > eom)
		RETERR(EMSGSIZE);
	DNS_NS_GET16(handle->_id, msg);
	if (msg + NS_INT16SZ > eom)
		RETERR(EMSGSIZE);
	DNS_NS_GET16(handle->_flags, msg);
	for (i = 0; i < ns_s_max; i++) {
		if (msg + NS_INT16SZ > eom)
			RETERR(EMSGSIZE);
		DNS_NS_GET16(handle->_counts[i], msg);
	}
	for (i = 0; i < ns_s_max; i++)
		if (handle->_counts[i] == 0)
			handle->_sections[i] = NULL;
		else {
			int b = dns_ns_skiprr(msg, eom, (ns_sect)i,
					  handle->_counts[i]);

			if (b < 0)
				return (-1);
			handle->_sections[i] = msg;
			msg += b;
		}
	if (msg != eom)
		RETERR(EMSGSIZE);
	setsection(handle, ns_s_max);
	return (0);
}

int
dns_ns_parserr(ns_msg *handle, ns_sect section, int rrnum, ns_rr *rr) {
	int b;

	/* Make section right. */
	if (section < 0 || section >= ns_s_max)
		RETERR(ENODEV);
	if (section != handle->_sect)
		setsection(handle, section);

	/* Make rrnum right. */
	if (rrnum == -1)
		rrnum = handle->_rrnum;
	if (rrnum < 0 || rrnum >= handle->_counts[(int)section])
		RETERR(ENODEV);
	if (rrnum < handle->_rrnum)
		setsection(handle, section);
	if (rrnum > handle->_rrnum) {
		b = dns_ns_skiprr(NS_PTR(handle), handle->_eom, section,
			      rrnum - handle->_rrnum);

		if (b < 0)
			return (-1);
		NS_PTR(handle) += b;
		handle->_rrnum = rrnum;
	}

	/* Do the parse. */
	b = dn_expand(handle->_msg, handle->_eom,
		      NS_PTR(handle), rr->name, NS_MAXDNAME);
	if (b < 0)
		return (-1);
	NS_PTR(handle) += b;
	if (NS_PTR(handle) + NS_INT16SZ + NS_INT16SZ > handle->_eom)
		RETERR(EMSGSIZE);
	DNS_NS_GET16(rr->type, NS_PTR(handle));
	DNS_NS_GET16(rr->rr_class, NS_PTR(handle));
	if (section == ns_s_qd) {
		rr->ttl = 0;
		rr->rdlength = 0;
		rr->rdata = NULL;
	} else {
                if (NS_PTR(handle) + NS_INT32SZ + NS_INT16SZ > handle->_eom)
			RETERR(EMSGSIZE);
		DNS_NS_GET32(rr->ttl, NS_PTR(handle));
		DNS_NS_GET16(rr->rdlength, NS_PTR(handle));
		if (NS_PTR(handle) + rr->rdlength > handle->_eom)
			RETERR(EMSGSIZE);
		rr->rdata = NS_PTR(handle);
		NS_PTR(handle) += rr->rdlength;
	}
	if (++handle->_rrnum > handle->_counts[(int)section])
		setsection(handle, (ns_sect)((int)section + 1));

	/* All done. */
	return (0);
}

/* Private. */

static void
setsection(ns_msg *msg, ns_sect sect) {
	msg->_sect = sect;
	if (sect == ns_s_max) {
		msg->_rrnum = -1;
		NS_PTR(msg) = NULL;
	} else {
		msg->_rrnum = 0;
		NS_PTR(msg) = msg->_sections[(int)sect];
	}
}

#endif

--- NEW FILE: query.c ---
/*
 * DNS support
 *
 * Copyright (C) 2006 Hans Leidekker
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"

#include <stdarg.h>
#include <string.h>
#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "winnls.h"
#include "wine-lgpl/windns.h"
#include "nb30.h"

#include "dnsapi.h"

WINE_DEFAULT_DEBUG_CHANNEL(dnsapi);

#ifdef HAVE_RESOLV

static CRITICAL_SECTION resolver_cs;
static CRITICAL_SECTION_DEBUG resolver_cs_debug =
{
    0, 0, &resolver_cs,
    { &resolver_cs_debug.ProcessLocksList,
      &resolver_cs_debug.ProcessLocksList },
      0, 0, { (DWORD_PTR)(__FILE__ ": resolver_cs") }
};
static CRITICAL_SECTION resolver_cs = { &resolver_cs_debug, -1, 0, 0, 0, 0 };

#define LOCK_RESOLVER()     do { EnterCriticalSection( &resolver_cs ); } while (0)
#define UNLOCK_RESOLVER()   do { LeaveCriticalSection( &resolver_cs ); } while (0)

static int resolver_initialised;

/* call res_init() just once because of a bug in Mac OS X 10.4 */
static void initialise_resolver( void )
{
    if (!resolver_initialised)
    {
        res_init();
        resolver_initialised = 1;
    }
}

static const char *dns_section_to_str( ns_sect section )
{
    switch (section)
    {
    case ns_s_qd:   return "Question";
    case ns_s_an:   return "Answer";
    case ns_s_ns:   return "Authority";
    case ns_s_ar:   return "Additional";
    default:
    {
        static char tmp[5];
        FIXME( "unknown section: 0x%02x\n", section );
        sprintf( tmp, "0x%02x", section );
        return tmp;
    }
    }
}

static unsigned long dns_map_options( DWORD options )
{
    unsigned long ret = 0;
            
    if (options == DNS_QUERY_STANDARD)
        return RES_DEFAULT;

    if (options & DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE)
        ret |= RES_IGNTC;
    if (options & DNS_QUERY_USE_TCP_ONLY)
        ret |= RES_USEVC;
    if (options & DNS_QUERY_NO_RECURSION)
        ret &= ~RES_RECURSE;
    if (options & DNS_QUERY_NO_LOCAL_NAME)
        ret &= ~RES_DNSRCH;
    if (options & DNS_QUERY_NO_HOSTS_FILE)
        ret |= RES_NOALIASES;
    if (options & DNS_QUERY_TREAT_AS_FQDN)
        ret &= ~RES_DEFNAMES;

    if (options & DNS_QUERY_DONT_RESET_TTL_VALUES)
        FIXME( "option DNS_QUERY_DONT_RESET_TTL_VALUES not implemented\n" );
    if (options & DNS_QUERY_RESERVED)
        FIXME( "option DNS_QUERY_RESERVED not implemented\n" );
    if (options & DNS_QUERY_WIRE_ONLY)
        FIXME( "option DNS_QUERY_WIRE_ONLY not implemented\n" );
    if (options & DNS_QUERY_NO_WIRE_QUERY)
        FIXME( "option DNS_QUERY_NO_WIRE_QUERY not implemented\n" );
    if (options & DNS_QUERY_BYPASS_CACHE)
        FIXME( "option DNS_QUERY_BYPASS_CACHE not implemented\n" );
    if (options & DNS_QUERY_RETURN_MESSAGE)
        FIXME( "option DNS_QUERY_RETURN_MESSAGE not implemented\n" );

    if (options & DNS_QUERY_NO_NETBT)
        TRACE( "netbios query disabled\n" );

    return ret;
}

static DNS_STATUS dns_map_error( int error )
{
    switch (error)
    {
    case ns_r_noerror:  return ERROR_SUCCESS;
    case ns_r_formerr:  return DNS_ERROR_RCODE_FORMAT_ERROR;
    case ns_r_servfail: return DNS_ERROR_RCODE_SERVER_FAILURE;
    case ns_r_nxdomain: return DNS_ERROR_RCODE_NAME_ERROR;
    case ns_r_notimpl:  return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
    case ns_r_refused:  return DNS_ERROR_RCODE_REFUSED;
    case ns_r_yxdomain: return DNS_ERROR_RCODE_YXDOMAIN;
    case ns_r_yxrrset:  return DNS_ERROR_RCODE_YXRRSET;
    case ns_r_nxrrset:  return DNS_ERROR_RCODE_NXRRSET;
    case ns_r_notauth:  return DNS_ERROR_RCODE_NOTAUTH;
    case ns_r_notzone:  return DNS_ERROR_RCODE_NOTZONE;
    default:
        FIXME( "unmapped error code: %d\n", error );
        return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
    }
}

static DNS_STATUS dns_map_h_errno( int error )
{
    switch (error)
    {
    case NO_DATA:
    case HOST_NOT_FOUND: return DNS_ERROR_RCODE_NAME_ERROR;
    case TRY_AGAIN:      return DNS_ERROR_RCODE_SERVER_FAILURE;
    case NO_RECOVERY:    return DNS_ERROR_RCODE_REFUSED;
    case NETDB_INTERNAL: return DNS_ERROR_RCODE;
    default:
        FIXME( "unmapped error code: %d\n", error );
        return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
    }
}

static char *dns_dname_from_msg( ns_msg msg, const unsigned char *pos )
{
    int len;
    char *str, dname[NS_MAXDNAME] = ".";

    /* returns *compressed* length, ignore it */
    len = dns_ns_name_uncompress( ns_msg_base( msg ), ns_msg_end( msg ),
                                  pos, dname, sizeof(dname) );

    len = strlen( dname );
    str = dns_alloc( len + 1 );
    if (str) strcpy( str, dname );
    return str;
}

static char *dns_str_from_rdata( const unsigned char *rdata )
{
    char *str;
    unsigned int len = rdata[0];

    str = dns_alloc( len + 1 );
    if (str)
    {
        memcpy( str, ++rdata, len );
        str[len] = '\0';
    }
    return str;
}

static unsigned int dns_get_record_size( const ns_rr *rr )
{
    const unsigned char *pos = rr->rdata;
    unsigned int num = 0, size = sizeof(DNS_RECORDA);

    switch (rr->type)
    {
    case ns_t_key:
    {
        pos += sizeof(WORD) + sizeof(BYTE) + sizeof(BYTE);
        size += rr->rdata + rr->rdlength - pos - 1;
        break;
    }
    case ns_t_sig:
    {
        pos += sizeof(PCHAR) + sizeof(WORD) + 2 * sizeof(BYTE);
        pos += 3 * sizeof(DWORD) + 2 * sizeof(WORD);
        size += rr->rdata + rr->rdlength - pos - 1;
        break;
    }
    case ns_t_hinfo:
    case ns_t_isdn:
    case ns_t_txt:
    case ns_t_x25:
    {
        while (pos[0] && pos < rr->rdata + rr->rdlength)
        {
            num++;
            pos += pos[0] + 1;
        }
        size += (num - 1) * sizeof(PCHAR);
        break;
    }
    case ns_t_null:
    {
        size += rr->rdlength - 1;
        break;
    }
    case ns_t_nxt:
    case ns_t_wks:
    case 0xff01:  /* WINS */
    {
        FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
        break;
    }
    default:
        break;
    }
    return size;
}

static DNS_STATUS dns_copy_rdata( ns_msg msg, const ns_rr *rr, DNS_RECORDA *r, WORD *dlen )
{
    DNS_STATUS ret = ERROR_SUCCESS;
    const unsigned char *pos = rr->rdata;
    unsigned int i, size;

    switch (rr->type)
    {
    case ns_t_a:
    {
        r->Data.A.IpAddress = *(const DWORD *)pos;
        *dlen = sizeof(DNS_A_DATA);
        break; 
    }
    case ns_t_aaaa:
    {
        for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++)
        {
            r->Data.AAAA.Ip6Address.IP6Dword[i] = *(const DWORD *)pos;
            pos += sizeof(DWORD);
        }

        *dlen = sizeof(DNS_AAAA_DATA);
        break;
    }
    case ns_t_key:
    {
        /* FIXME: byte order? */
        r->Data.KEY.wFlags      = *(const WORD *)pos;   pos += sizeof(WORD);
        r->Data.KEY.chProtocol  = *(const BYTE *)pos++;
        r->Data.KEY.chAlgorithm = *(const BYTE *)pos++;

        size = rr->rdata + rr->rdlength - pos;

        for (i = 0; i < size; i++)
            r->Data.KEY.Key[i] = *(const BYTE *)pos++;

        *dlen = sizeof(DNS_KEY_DATA) + (size - 1) * sizeof(BYTE);
        break;
    }
    case ns_t_rp:
    case ns_t_minfo:
    {
        r->Data.MINFO.pNameMailbox = dns_dname_from_msg( msg, pos );
        if (!r->Data.MINFO.pNameMailbox) return ERROR_NOT_ENOUGH_MEMORY;

        if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
            return DNS_ERROR_BAD_PACKET;

        r->Data.MINFO.pNameErrorsMailbox = dns_dname_from_msg( msg, pos );
        if (!r->Data.MINFO.pNameErrorsMailbox)
        {
            dns_free( r->Data.MINFO.pNameMailbox ); 
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        *dlen = sizeof(DNS_MINFO_DATAA);
        break; 
    }
    case ns_t_afsdb:
    case ns_t_rt:
    case ns_t_mx:
    {
        r->Data.MX.wPreference = ntohs( *(const WORD *)pos );
        r->Data.MX.pNameExchange = dns_dname_from_msg( msg, pos + sizeof(WORD) );
        if (!r->Data.MX.pNameExchange) return ERROR_NOT_ENOUGH_MEMORY;

        *dlen = sizeof(DNS_MX_DATAA);
        break; 
    }
    case ns_t_null:
    {
        r->Data.Null.dwByteCount = rr->rdlength;
        memcpy( r->Data.Null.Data, rr->rdata, rr->rdlength );

        *dlen = sizeof(DNS_NULL_DATA) + rr->rdlength - 1;
        break;
    }
    case ns_t_cname:
    case ns_t_ns:
    case ns_t_mb:
    case ns_t_md:
    case ns_t_mf:
    case ns_t_mg:
    case ns_t_mr:
    case ns_t_ptr:
    {
        r->Data.PTR.pNameHost = dns_dname_from_msg( msg, pos );
        if (!r->Data.PTR.pNameHost) return ERROR_NOT_ENOUGH_MEMORY;

        *dlen = sizeof(DNS_PTR_DATAA);
        break;
    }
    case ns_t_sig:
    {
        r->Data.SIG.pNameSigner = dns_dname_from_msg( msg, pos );
        if (!r->Data.SIG.pNameSigner) return ERROR_NOT_ENOUGH_MEMORY;

        if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
            return DNS_ERROR_BAD_PACKET;

        /* FIXME: byte order? */
        r->Data.SIG.wTypeCovered  = *(const WORD *)pos;   pos += sizeof(WORD);
        r->Data.SIG.chAlgorithm   = *(const BYTE *)pos++;
        r->Data.SIG.chLabelCount  = *(const BYTE *)pos++;
        r->Data.SIG.dwOriginalTtl = *(const DWORD *)pos;  pos += sizeof(DWORD);
        r->Data.SIG.dwExpiration  = *(const DWORD *)pos;  pos += sizeof(DWORD);
        r->Data.SIG.dwTimeSigned  = *(const DWORD *)pos;  pos += sizeof(DWORD);
        r->Data.SIG.wKeyTag       = *(const WORD *)pos;

        size = rr->rdata + rr->rdlength - pos;

        for (i = 0; i < size; i++)
            r->Data.SIG.Signature[i] = *(const BYTE *)pos++;

        *dlen = sizeof(DNS_SIG_DATAA) + (size - 1) * sizeof(BYTE);
        break; 
    }
    case ns_t_soa:
    {
        r->Data.SOA.pNamePrimaryServer = dns_dname_from_msg( msg, pos );
        if (!r->Data.SOA.pNamePrimaryServer) return ERROR_NOT_ENOUGH_MEMORY;

        if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
            return DNS_ERROR_BAD_PACKET;

        r->Data.SOA.pNameAdministrator = dns_dname_from_msg( msg, pos );
        if (!r->Data.SOA.pNameAdministrator)
        {
            dns_free( r->Data.SOA.pNamePrimaryServer ); 
            return ERROR_NOT_ENOUGH_MEMORY;
        }

        if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
            return DNS_ERROR_BAD_PACKET;

        r->Data.SOA.dwSerialNo   = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD);
        r->Data.SOA.dwRefresh    = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD);
        r->Data.SOA.dwRetry      = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD);
        r->Data.SOA.dwExpire     = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD);
        r->Data.SOA.dwDefaultTtl = ntohl( *(const DWORD *)pos ); pos += sizeof(DWORD);

        *dlen = sizeof(DNS_SOA_DATAA);
        break; 
    }
    case ns_t_srv:
    {
        r->Data.SRV.wPriority = ntohs( *(const WORD *)pos ); pos += sizeof(WORD);
        r->Data.SRV.wWeight   = ntohs( *(const WORD *)pos ); pos += sizeof(WORD);
        r->Data.SRV.wPort     = ntohs( *(const WORD *)pos ); pos += sizeof(WORD);

        r->Data.SRV.pNameTarget = dns_dname_from_msg( msg, pos );
        if (!r->Data.SRV.pNameTarget) return ERROR_NOT_ENOUGH_MEMORY;

        *dlen = sizeof(DNS_SRV_DATAA);
        break; 
    }
    case ns_t_hinfo:
    case ns_t_isdn:
    case ns_t_x25:
    case ns_t_txt:
    {
        i = 0;
        while (pos[0] && pos < rr->rdata + rr->rdlength)
        {
            r->Data.TXT.pStringArray[i] = dns_str_from_rdata( pos );
            if (!r->Data.TXT.pStringArray[i])
            {
                while (i > 0) dns_free( r->Data.TXT.pStringArray[--i] );
                return ERROR_NOT_ENOUGH_MEMORY;
            }
            i++;
            pos += pos[0] + 1;
        }
        r->Data.TXT.dwStringCount = i;
        *dlen = sizeof(DNS_TXT_DATAA) + (i - 1) * sizeof(PCHAR);
        break;
    }
    case ns_t_atma:
    case ns_t_loc:
    case ns_t_nxt:
    case ns_t_tsig:
    case ns_t_wks:
    case 0x00f9:  /* TKEY */
    case 0xff01:  /* WINS */
    case 0xff02:  /* WINSR */
    default:
        FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
        return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
    }

    return ret;
} 

static DNS_STATUS dns_copy_record( ns_msg msg, ns_sect section,
                                   unsigned short num, DNS_RECORDA **recp )
{
    DNS_STATUS ret;
    DNS_RECORDA *record;
    WORD dlen;
    ns_rr rr;

    if (dns_ns_parserr( &msg, section, num, &rr ) < 0)
        return DNS_ERROR_BAD_PACKET;

    if (!(record = dns_zero_alloc( dns_get_record_size( &rr ) )))
        return ERROR_NOT_ENOUGH_MEMORY;

    record->pName = dns_strdup_u( rr.name );
    if (!record->pName)
    {
        dns_free( record );
        return ERROR_NOT_ENOUGH_MEMORY;
    }

    record->wType = rr.type;
    record->Flags.S.Section = section;
    record->Flags.S.CharSet = DnsCharSetUtf8;
    record->dwTtl = rr.ttl;

    if ((ret = dns_copy_rdata( msg, &rr, record, &dlen )))
    {
        dns_free( record->pName );
        dns_free( record );
        return ret;
    }
    record->wDataLength = dlen;
    *recp = record;

    TRACE( "found %s record in %s section\n",
           dns_type_to_str( rr.type ), dns_section_to_str( section ) );
    return ERROR_SUCCESS;
}

#define DEFAULT_TTL  1200

static DNS_STATUS dns_do_query_netbios( PCSTR name, DNS_RECORDA **recp )
{
    NCB ncb;
    UCHAR ret;
    DNS_RRSET rrset;
    FIND_NAME_BUFFER *buffer;
    FIND_NAME_HEADER *header;
    DNS_RECORDA *record = NULL;
    unsigned int i, len;
    DNS_STATUS status = ERROR_INVALID_NAME;

    len = strlen( name );
    if (len >= NCBNAMSZ) return DNS_ERROR_RCODE_NAME_ERROR;

    DNS_RRSET_INIT( rrset );

    memset( &ncb, 0, sizeof(ncb) );
    ncb.ncb_command = NCBFINDNAME;

    memset( ncb.ncb_callname, ' ', sizeof(ncb.ncb_callname) );
    memcpy( ncb.ncb_callname, name, len );
    ncb.ncb_callname[NCBNAMSZ - 1] = '\0';

    ret = Netbios( &ncb );
    if (ret != NRC_GOODRET) return ERROR_INVALID_NAME;

    header = (FIND_NAME_HEADER *)ncb.ncb_buffer;
    buffer = (FIND_NAME_BUFFER *)((char *)header + sizeof(FIND_NAME_HEADER));

    for (i = 0; i < header->node_count; i++) 
    {
        record = dns_zero_alloc( sizeof(DNS_RECORDA) );
        if (!record)
        {
            status = ERROR_NOT_ENOUGH_MEMORY;
            goto exit;
        }
        else
        {
            record->pName = dns_strdup_u( name );
            if (!record->pName)
            {
                status = ERROR_NOT_ENOUGH_MEMORY;
                goto exit;
            }

            record->wType = DNS_TYPE_A;
            record->Flags.S.Section = DnsSectionAnswer;
            record->Flags.S.CharSet = DnsCharSetUtf8;
            record->dwTtl = DEFAULT_TTL;

            /* FIXME: network byte order? */
            record->Data.A.IpAddress = *(DWORD *)((char *)buffer[i].destination_addr + 2);

            DNS_RRSET_ADD( rrset, (DNS_RECORD *)record );
        }
    }
    status = ERROR_SUCCESS;

exit:
    DNS_RRSET_TERMINATE( rrset );

    if (status != ERROR_SUCCESS)
        DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList );
    else
        *recp = (DNS_RECORDA *)rrset.pFirstRR;

    return status;
}

/*  The resolver lock must be held and res_init() must have been
 *  called before calling these three functions.
 */
static DNS_STATUS dns_set_serverlist( const IP4_ARRAY *addrs )
{
    unsigned int i;

    if (addrs->AddrCount > MAXNS) 
    {
        WARN( "too many servers: %d only using the first: %d\n",
              addrs->AddrCount, MAXNS );
        _res.nscount = MAXNS;
    }
    else _res.nscount = addrs->AddrCount;

    for (i = 0; i < _res.nscount; i++)
        _res.nsaddr_list[i].sin_addr.s_addr = addrs->AddrArray[i];

    return ERROR_SUCCESS;
}

static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len )
{
    unsigned int i, size;

    size = sizeof(IP4_ARRAY) + sizeof(IP4_ADDRESS) * (_res.nscount - 1);
    if (!addrs || *len < size)
    {
        *len = size;
        return ERROR_INSUFFICIENT_BUFFER;
    }

    addrs->AddrCount = _res.nscount;

    for (i = 0; i < _res.nscount; i++)
        addrs->AddrArray[i] = _res.nsaddr_list[i].sin_addr.s_addr;

    return ERROR_SUCCESS;
}

static DNS_STATUS dns_do_query( PCSTR name, WORD type, DWORD options,
                                PDNS_RECORDA *result )
{
    DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;

    unsigned int i, num;
    unsigned char answer[NS_PACKETSZ];
    ns_sect sections[] = { ns_s_an, ns_s_ar };
    ns_msg msg;

    DNS_RECORDA *record = NULL;
    DNS_RRSET rrset;
    int len;

    DNS_RRSET_INIT( rrset );

    len = res_query( name, ns_c_in, type, answer, sizeof(answer) );
    if (len < 0)
    {
        ret = dns_map_h_errno( h_errno );
        goto exit;
    }

    if (dns_ns_initparse( answer, len, &msg ) < 0)
    {
        ret = DNS_ERROR_BAD_PACKET;
        goto exit;
    }

#define RCODE_MASK 0x0f
    if ((msg._flags & RCODE_MASK) != ns_r_noerror)
    {
        ret = dns_map_error( msg._flags & RCODE_MASK );
        goto exit;
    }

    for (i = 0; i < sizeof(sections)/sizeof(sections[0]); i++)
    {
        for (num = 0; num < ns_msg_count( msg, sections[i] ); num++)
        {
            ret = dns_copy_record( msg, sections[i], num, &record );
            if (ret != ERROR_SUCCESS) goto exit;

            DNS_RRSET_ADD( rrset, (DNS_RECORD *)record );
        }
    }

exit:
    DNS_RRSET_TERMINATE( rrset );

    if (ret != ERROR_SUCCESS)
        DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList );
    else
        *result = (DNS_RECORDA *)rrset.pFirstRR;

    return ret;
}

#endif /* HAVE_RESOLV */

/******************************************************************************
 * DnsQuery_A           [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsQuery_A( PCSTR name, WORD type, DWORD options, PVOID servers,
                              PDNS_RECORDA *result, PVOID *reserved )
{
    WCHAR *nameW;
    DNS_RECORDW *resultW;
    DNS_STATUS status;

    TRACE( "(%s,%s,0x%08x,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
           options, servers, result, reserved );

    if (!name || !result)
        return ERROR_INVALID_PARAMETER;

    nameW = dns_strdup_aw( name );
    if (!nameW) return ERROR_NOT_ENOUGH_MEMORY;

    status = DnsQuery_W( nameW, type, options, servers, &resultW, reserved ); 

    if (status == ERROR_SUCCESS)
    {
        *result = (DNS_RECORDA *)DnsRecordSetCopyEx(
             (DNS_RECORD *)resultW, DnsCharSetUnicode, DnsCharSetAnsi );

        if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
        DnsRecordListFree( (DNS_RECORD *)resultW, DnsFreeRecordList );
    }

    dns_free( nameW );
    return status;
}

/******************************************************************************
 * DnsQuery_UTF8              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsQuery_UTF8( PCSTR name, WORD type, DWORD options, PVOID servers,
                                 PDNS_RECORDA *result, PVOID *reserved )
{
    DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;
#ifdef HAVE_RESOLV

    TRACE( "(%s,%s,0x%08x,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
           options, servers, result, reserved );

    if (!name || !result)
        return ERROR_INVALID_PARAMETER;

    LOCK_RESOLVER();

    initialise_resolver();
    _res.options |= dns_map_options( options );

    if (servers && (ret = dns_set_serverlist( servers )))
    {
        UNLOCK_RESOLVER();
        return ret;
    }

    ret = dns_do_query( name, type, options, result );

    if (ret == DNS_ERROR_RCODE_NAME_ERROR && type == DNS_TYPE_A &&
        !(options & DNS_QUERY_NO_NETBT))
    {
        TRACE( "dns lookup failed, trying netbios query\n" );
        ret = dns_do_query_netbios( name, result );
    }

    UNLOCK_RESOLVER();

#endif
    return ret;
}

/******************************************************************************
 * DnsQuery_W              [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsQuery_W( PCWSTR name, WORD type, DWORD options, PVOID servers,
                              PDNS_RECORDW *result, PVOID *reserved )
{
    char *nameU;
    DNS_RECORDA *resultA;
    DNS_STATUS status;

    TRACE( "(%s,%s,0x%08x,%p,%p,%p)\n", debugstr_w(name), dns_type_to_str( type ),
           options, servers, result, reserved );

    if (!name || !result)
        return ERROR_INVALID_PARAMETER;

    nameU = dns_strdup_wu( name );
    if (!nameU) return ERROR_NOT_ENOUGH_MEMORY;

    status = DnsQuery_UTF8( nameU, type, options, servers, &resultA, reserved ); 

    if (status == ERROR_SUCCESS)
    {
        *result = (DNS_RECORDW *)DnsRecordSetCopyEx(
            (DNS_RECORD *)resultA, DnsCharSetUtf8, DnsCharSetUnicode );

        if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
        DnsRecordListFree( (DNS_RECORD *)resultA, DnsFreeRecordList );
    }

    dns_free( nameU );
    return status;
}

static DNS_STATUS dns_get_hostname_a( COMPUTER_NAME_FORMAT format,
                                      PSTR buffer, PDWORD len )
{
    char name[256];
    DWORD size = sizeof(name);

    if (!GetComputerNameExA( format, name, &size ))
        return DNS_ERROR_NAME_DOES_NOT_EXIST;

    if (!buffer || (size = lstrlenA( name ) + 1) > *len)
    {
        *len = size;
        return ERROR_INSUFFICIENT_BUFFER;
    }

    lstrcpyA( buffer, name );
    return ERROR_SUCCESS;
}

static DNS_STATUS dns_get_hostname_w( COMPUTER_NAME_FORMAT format,
                                      PWSTR buffer, PDWORD len )
{
    WCHAR name[256];
    DWORD size = sizeof(name);

    if (!GetComputerNameExW( format, name, &size ))
        return DNS_ERROR_NAME_DOES_NOT_EXIST;

    if (!buffer || (size = lstrlenW( name ) + 1) > *len)
    {
        *len = size;
        return ERROR_INSUFFICIENT_BUFFER;
    }

    lstrcpyW( buffer, name );
    return ERROR_SUCCESS;
}

/******************************************************************************
 * DnsQueryConfig          [DNSAPI.@]
 *
 */
DNS_STATUS WINAPI DnsQueryConfig( DNS_CONFIG_TYPE config, DWORD flag, PWSTR adapter,
                                  PVOID reserved, PVOID buffer, PDWORD len )
{
    DNS_STATUS ret = ERROR_INVALID_PARAMETER;

    TRACE( "(%d,0x%08x,%s,%p,%p,%p)\n", config, flag, debugstr_w(adapter),
           reserved, buffer, len );

    if (!len) return ERROR_INVALID_PARAMETER;

    switch (config)
    {
    case DnsConfigDnsServerList:
    {
#ifdef HAVE_RESOLV
        LOCK_RESOLVER();

        initialise_resolver();
        ret = dns_get_serverlist( buffer, len );

        UNLOCK_RESOLVER();
        break;
#else
        WARN( "compiled without resolver support\n" );
        break;
#endif
    }
    case DnsConfigHostName_A:
    case DnsConfigHostName_UTF8:
        return dns_get_hostname_a( ComputerNameDnsHostname, buffer, len );

    case DnsConfigFullHostName_A:
    case DnsConfigFullHostName_UTF8:
        return dns_get_hostname_a( ComputerNameDnsFullyQualified, buffer, len );

    case DnsConfigPrimaryDomainName_A:
    case DnsConfigPrimaryDomainName_UTF8:
        return dns_get_hostname_a( ComputerNameDnsDomain, buffer, len );

    case DnsConfigHostName_W:
        return dns_get_hostname_w( ComputerNameDnsHostname, buffer, len );

    case DnsConfigFullHostName_W:
        return dns_get_hostname_w( ComputerNameDnsFullyQualified, buffer, len );

    case DnsConfigPrimaryDomainName_W:
        return dns_get_hostname_w( ComputerNameDnsDomain, buffer, len );

    case DnsConfigAdapterDomainName_A:
    case DnsConfigAdapterDomainName_W:
    case DnsConfigAdapterDomainName_UTF8:
    case DnsConfigSearchList:
    case DnsConfigAdapterInfo:
    case DnsConfigPrimaryHostNameRegistrationEnabled:
    case DnsConfigAdapterHostNameRegistrationEnabled:
    case DnsConfigAddressRegistrationMaxCount:
        FIXME( "unimplemented config type %d\n", config );
        break;

    default:
        WARN( "unknown config type: %d\n", config );
        break;
    }
    return ret;
}

--- NEW FILE: record.c ---
/*
 * DNS support
 *
 * Copyright (C) 2006 Hans Leidekker
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "config.h"
#include "wine/port.h"
#include "wine/debug.h"

#include <stdarg.h>
#include <string.h>
#include <sys/types.h>

#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_NAMESER_H
# include <arpa/nameser.h>
#endif
#ifdef HAVE_RESOLV_H
# include <resolv.h>
#endif

#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "wine-lgpl/windns.h"

#include "dnsapi.h"

WINE_DEFAULT_DEBUG_CHANNEL(dnsapi);

const char *dns_type_to_str( unsigned short type )
{
    switch (type)
    {
#define X(x)    case (x): return #x;
    X(DNS_TYPE_ZERO)
    X(DNS_TYPE_A)
    X(DNS_TYPE_NS)
    X(DNS_TYPE_MD)
    X(DNS_TYPE_MF)
    X(DNS_TYPE_CNAME)
    X(DNS_TYPE_SOA)
    X(DNS_TYPE_MB)
    X(DNS_TYPE_MG)
    X(DNS_TYPE_MR)
    X(DNS_TYPE_NULL)
    X(DNS_TYPE_WKS)
    X(DNS_TYPE_PTR)
    X(DNS_TYPE_HINFO)
    X(DNS_TYPE_MINFO)
    X(DNS_TYPE_MX)
    X(DNS_TYPE_TEXT)
    X(DNS_TYPE_RP)
    X(DNS_TYPE_AFSDB)
    X(DNS_TYPE_X25)
    X(DNS_TYPE_ISDN)
    X(DNS_TYPE_RT)
    X(DNS_TYPE_NSAP)
    X(DNS_TYPE_NSAPPTR)
    X(DNS_TYPE_SIG)
    X(DNS_TYPE_KEY)
    X(DNS_TYPE_PX)
    X(DNS_TYPE_GPOS)
    X(DNS_TYPE_AAAA)
    X(DNS_TYPE_LOC)
    X(DNS_TYPE_NXT)
    X(DNS_TYPE_EID)
    X(DNS_TYPE_NIMLOC)
    X(DNS_TYPE_SRV)
    X(DNS_TYPE_ATMA)
    X(DNS_TYPE_NAPTR)
    X(DNS_TYPE_KX)
    X(DNS_TYPE_CERT)
    X(DNS_TYPE_A6)
    X(DNS_TYPE_DNAME)
    X(DNS_TYPE_SINK)
    X(DNS_TYPE_OPT)
    X(DNS_TYPE_UINFO)
    X(DNS_TYPE_UID)
    X(DNS_TYPE_GID)
    X(DNS_TYPE_UNSPEC)
    X(DNS_TYPE_ADDRS)
    X(DNS_TYPE_TKEY)
    X(DNS_TYPE_TSIG)
    X(DNS_TYPE_IXFR)
    X(DNS_TYPE_AXFR)
    X(DNS_TYPE_MAILB)
    X(DNS_TYPE_MAILA)
    X(DNS_TYPE_ANY)
    X(DNS_TYPE_WINS)
    X(DNS_TYPE_WINSR)
#undef X
    default: { static char tmp[7]; sprintf( tmp, "0x%04x", type ); return tmp; }
    }
}

static int dns_strcmpX( LPCVOID str1, LPCVOID str2, BOOL wide )
{
    if (wide)
        return lstrcmpiW( str1, str2 );
    else
        return lstrcmpiA( str1, str2 );
}

/******************************************************************************
 * DnsRecordCompare                        [DNSAPI.@]
 *
 */
BOOL WINAPI DnsRecordCompare( PDNS_RECORD r1, PDNS_RECORD r2 )
{
    BOOL wide;
    unsigned int i;

    TRACE( "(%p,%p)\n", r1, r2 );

    if (r1->wType       != r2->wType       ||
        r1->wDataLength != r2->wDataLength ||
        r1->Flags.DW    != r2->Flags.DW    ||
        r1->dwReserved  != r2->dwReserved) return FALSE;

    wide = (r1->Flags.S.CharSet == DnsCharSetUnicode) ? TRUE : FALSE;
    if (dns_strcmpX( r1->pName, r2->pName, wide )) return FALSE;

    switch (r1->wType)
    {
    case DNS_TYPE_A:
    {
        if (r1->Data.A.IpAddress != r2->Data.A.IpAddress) return FALSE;
        break;
    }
    case DNS_TYPE_SOA:
    {
        if (r1->Data.SOA.dwSerialNo   != r2->Data.SOA.dwSerialNo ||
            r1->Data.SOA.dwRefresh    != r2->Data.SOA.dwRefresh  ||
            r1->Data.SOA.dwRetry      != r2->Data.SOA.dwRetry    ||
            r1->Data.SOA.dwExpire     != r2->Data.SOA.dwExpire   ||
            r1->Data.SOA.dwDefaultTtl != r2->Data.SOA.dwDefaultTtl)
            return FALSE;
        if (dns_strcmpX( r1->Data.SOA.pNamePrimaryServer,
                         r2->Data.SOA.pNamePrimaryServer, wide ) ||
            dns_strcmpX( r1->Data.SOA.pNameAdministrator,
                         r2->Data.SOA.pNameAdministrator, wide ))
            return FALSE;
        break;
    }
    case DNS_TYPE_PTR:
    case DNS_TYPE_NS:
    case DNS_TYPE_CNAME:
    case DNS_TYPE_MB:
    case DNS_TYPE_MD:
    case DNS_TYPE_MF:
    case DNS_TYPE_MG:
    case DNS_TYPE_MR:
    {
        if (dns_strcmpX( r1->Data.PTR.pNameHost,
                         r2->Data.PTR.pNameHost, wide )) return FALSE;
        break;
    }
    case DNS_TYPE_MINFO:
    case DNS_TYPE_RP:
    {
        if (dns_strcmpX( r1->Data.MINFO.pNameMailbox,
                         r2->Data.MINFO.pNameMailbox, wide ) ||
            dns_strcmpX( r1->Data.MINFO.pNameErrorsMailbox,
                         r2->Data.MINFO.pNameErrorsMailbox, wide ))
            return FALSE;
        break;
    }
    case DNS_TYPE_MX:
    case DNS_TYPE_AFSDB:
    case DNS_TYPE_RT:
    {
        if (r1->Data.MX.wPreference != r2->Data.MX.wPreference)
            return FALSE;
        if (dns_strcmpX( r1->Data.MX.pNameExchange,
                         r2->Data.MX.pNameExchange, wide ))
            return FALSE;
        break;
    }
    case DNS_TYPE_HINFO:
    case DNS_TYPE_ISDN:
    case DNS_TYPE_TEXT:
    case DNS_TYPE_X25:
    {
        if (r1->Data.TXT.dwStringCount != r2->Data.TXT.dwStringCount)
            return FALSE;
        for (i = 0; i < r1->Data.TXT.dwStringCount; i++)
        {
            if (dns_strcmpX( r1->Data.TXT.pStringArray[i],
                             r2->Data.TXT.pStringArray[i], wide ))
                return FALSE;
        }
        break;
    }
    case DNS_TYPE_NULL:
    {
        if (r1->Data.Null.dwByteCount != r2->Data.Null.dwByteCount)
            return FALSE;
        if (memcmp( r1->Data.Null.Data,
                    r2->Data.Null.Data, r1->Data.Null.dwByteCount ))
            return FALSE;
        break;
    }
    case DNS_TYPE_AAAA:
    {
        for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++)
        {
            if (r1->Data.AAAA.Ip6Address.IP6Dword[i] !=
                r2->Data.AAAA.Ip6Address.IP6Dword[i]) return FALSE;
        }
        break;
    }
    case DNS_TYPE_KEY:
    {
        if (r1->Data.KEY.wFlags      != r2->Data.KEY.wFlags      ||
            r1->Data.KEY.chProtocol  != r2->Data.KEY.chProtocol  ||
            r1->Data.KEY.chAlgorithm != r2->Data.KEY.chAlgorithm)
            return FALSE;
        if (memcmp( r1->Data.KEY.Key, r2->Data.KEY.Key,
                    r1->wDataLength - sizeof(DNS_KEY_DATA) + 1 ))
            return FALSE;
        break;
    }
    case DNS_TYPE_SIG:
    {
        if (dns_strcmpX( r1->Data.SIG.pNameSigner,
                         r2->Data.SIG.pNameSigner, wide ))
            return FALSE;
        if (r1->Data.SIG.wTypeCovered  != r2->Data.SIG.wTypeCovered  ||
            r1->Data.SIG.chAlgorithm   != r2->Data.SIG.chAlgorithm   ||
            r1->Data.SIG.chLabelCount  != r2->Data.SIG.chLabelCount  ||
            r1->Data.SIG.dwOriginalTtl != r2->Data.SIG.dwOriginalTtl ||
            r1->Data.SIG.dwExpiration  != r2->Data.SIG.dwExpiration  ||
            r1->Data.SIG.dwTimeSigned  != r2->Data.SIG.dwTimeSigned  ||
            r1->Data.SIG.wKeyTag       != r2->Data.SIG.wKeyTag)
            return FALSE;
        if (memcmp( r1->Data.SIG.Signature, r2->Data.SIG.Signature,
                    r1->wDataLength - sizeof(DNS_SIG_DATAA) + 1 ))
            return FALSE;
        break;
    }
    case DNS_TYPE_ATMA:
    {
        if (r1->Data.ATMA.AddressType != r2->Data.ATMA.AddressType)
            return FALSE;
        for (i = 0; i < DNS_ATMA_MAX_ADDR_LENGTH; i++)
        {
            if (r1->Data.ATMA.Address[i] != r2->Data.ATMA.Address[i])
                return FALSE;
        }
        break;
    }
    case DNS_TYPE_NXT:
    {
        if (dns_strcmpX( r1->Data.NXT.pNameNext,
                         r2->Data.NXT.pNameNext, wide )) return FALSE;
        if (r1->Data.NXT.wNumTypes != r2->Data.NXT.wNumTypes) return FALSE;
        if (memcmp( r1->Data.NXT.wTypes, r2->Data.NXT.wTypes,
                    r1->wDataLength - sizeof(DNS_NXT_DATAA) + sizeof(WORD) ))
            return FALSE;
        break;
    }
    case DNS_TYPE_SRV:
    {
        if (dns_strcmpX( r1->Data.SRV.pNameTarget,
                         r2->Data.SRV.pNameTarget, wide )) return FALSE;
        if (r1->Data.SRV.wPriority != r2->Data.SRV.wPriority ||
            r1->Data.SRV.wWeight   != r2->Data.SRV.wWeight   ||
            r1->Data.SRV.wPort     != r2->Data.SRV.wPort)
            return FALSE;
        break;
    }
    case DNS_TYPE_TKEY:
    {
        if (dns_strcmpX( r1->Data.TKEY.pNameAlgorithm,
                         r2->Data.TKEY.pNameAlgorithm, wide ))
            return FALSE;
        if (r1->Data.TKEY.dwCreateTime    != r2->Data.TKEY.dwCreateTime     ||
            r1->Data.TKEY.dwExpireTime    != r2->Data.TKEY.dwExpireTime     ||
            r1->Data.TKEY.wMode           != r2->Data.TKEY.wMode            ||
            r1->Data.TKEY.wError          != r2->Data.TKEY.wError           ||
            r1->Data.TKEY.wKeyLength      != r2->Data.TKEY.wKeyLength       ||
            r1->Data.TKEY.wOtherLength    != r2->Data.TKEY.wOtherLength     ||
            r1->Data.TKEY.cAlgNameLength  != r2->Data.TKEY.cAlgNameLength   ||
            r1->Data.TKEY.bPacketPointers != r2->Data.TKEY.bPacketPointers)
            return FALSE;

        /* FIXME: ignoring pAlgorithmPacket field */
        if (memcmp( r1->Data.TKEY.pKey, r2->Data.TKEY.pKey,
                    r1->Data.TKEY.wKeyLength ) ||
            memcmp( r1->Data.TKEY.pOtherData, r2->Data.TKEY.pOtherData,
                    r1->Data.TKEY.wOtherLength )) return FALSE;
        break;
    }
    case DNS_TYPE_TSIG:
    {
        if (dns_strcmpX( r1->Data.TSIG.pNameAlgorithm,
                         r2->Data.TSIG.pNameAlgorithm, wide ))
            return FALSE;
        if (r1->Data.TSIG.i64CreateTime   != r2->Data.TSIG.i64CreateTime    ||
            r1->Data.TSIG.wFudgeTime      != r2->Data.TSIG.wFudgeTime       ||
            r1->Data.TSIG.wOriginalXid    != r2->Data.TSIG.wOriginalXid     ||
            r1->Data.TSIG.wError          != r2->Data.TSIG.wError           ||
            r1->Data.TSIG.wSigLength      != r2->Data.TSIG.wSigLength       ||
            r1->Data.TSIG.wOtherLength    != r2->Data.TSIG.wOtherLength     ||
            r1->Data.TSIG.cAlgNameLength  != r2->Data.TSIG.cAlgNameLength   ||
            r1->Data.TSIG.bPacketPointers != r2->Data.TSIG.bPacketPointers)
            return FALSE;

        /* FIXME: ignoring pAlgorithmPacket field */
        if (memcmp( r1->Data.TSIG.pSignature, r2->Data.TSIG.pSignature,
                    r1->Data.TSIG.wSigLength ) ||
            memcmp( r1->Data.TSIG.pOtherData, r2->Data.TSIG.pOtherData,
                    r1->Data.TSIG.wOtherLength )) return FALSE;
        break;
    }
    case DNS_TYPE_WINS:
    {
        if (r1->Data.WINS.dwMappingFlag    != r2->Data.WINS.dwMappingFlag   ||
            r1->Data.WINS.dwLookupTimeout  != r2->Data.WINS.dwLookupTimeout ||
            r1->Data.WINS.dwCacheTimeout   != r2->Data.WINS.dwCacheTimeout  ||
            r1->Data.WINS.cWinsServerCount != r2->Data.WINS.cWinsServerCount)
            return FALSE;
        if (memcmp( r1->Data.WINS.WinsServers, r2->Data.WINS.WinsServers,
                    r1->wDataLength - sizeof(DNS_WINS_DATA) + sizeof(IP4_ADDRESS) ))
            return FALSE;
        break;
    }
    case DNS_TYPE_WINSR:
    {
        if (r1->Data.WINSR.dwMappingFlag   != r2->Data.WINSR.dwMappingFlag   ||
            r1->Data.WINSR.dwLookupTimeout != r2->Data.WINSR.dwLookupTimeout ||
            r1->Data.WINSR.dwCacheTimeout  != r2->Data.WINSR.dwCacheTimeout)
            return FALSE;
        if (dns_strcmpX( r1->Data.WINSR.pNameResultDomain,
                         r2->Data.WINSR.pNameResultDomain, wide ))
            return FALSE;
        break;
    }
    default:
        FIXME( "unknown type: %s\n", dns_type_to_str( r1->wType ) );
        return FALSE;
    }
    return TRUE;
}

static LPVOID dns_strcpyX( LPCVOID src, DNS_CHARSET in, DNS_CHARSET out )
{
    switch (in)
    {
    case DnsCharSetUnicode:
    {
        switch (out)
        {
        case DnsCharSetUnicode: return dns_strdup_w( src );
        case DnsCharSetUtf8:    return dns_strdup_wu( src );
        case DnsCharSetAnsi:    return dns_strdup_wa( src );
        default:
            WARN( "unhandled target charset: %d\n", out );
            break;
        }
    }
    case DnsCharSetUtf8:
        switch (out)
        {
        case DnsCharSetUnicode: return dns_strdup_uw( src );
        case DnsCharSetUtf8:    return dns_strdup_u( src );
        case DnsCharSetAnsi:    return dns_strdup_ua( src );
        default:
            WARN( "unhandled target charset: %d\n", out );
            break;
        }
    case DnsCharSetAnsi:
        switch (out)
        {
        case DnsCharSetUnicode: return dns_strdup_aw( src );
        case DnsCharSetUtf8:    return dns_strdup_au( src );
        case DnsCharSetAnsi:    return dns_strdup_a( src );
        default:
            WARN( "unhandled target charset: %d\n", out );
            break;
        }
    default:
        WARN( "unhandled source charset: %d\n", in );
        break;
    }
    return NULL;
}

/******************************************************************************
 * DnsRecordCopyEx                         [DNSAPI.@]
 *
 */
PDNS_RECORD WINAPI DnsRecordCopyEx( PDNS_RECORD src, DNS_CHARSET in, DNS_CHARSET out )
{
    DNS_RECORD *dst;
    unsigned int i, size;

    TRACE( "(%p,%d,%d)\n", src, in, out );

    size = FIELD_OFFSET(DNS_RECORD, Data) + src->wDataLength;
    dst = dns_zero_alloc( size );
    if (!dst) return NULL;

    memcpy( dst, src, size );

    if (src->Flags.S.CharSet == DnsCharSetUtf8 ||
        src->Flags.S.CharSet == DnsCharSetAnsi ||
        src->Flags.S.CharSet == DnsCharSetUnicode) in = src->Flags.S.CharSet;

    dst->Flags.S.CharSet = out;
    dst->pName = dns_strcpyX( src->pName, in, out );
    if (!dst->pName) goto error;

    switch (src->wType)
    {
    case DNS_TYPE_HINFO:
    case DNS_TYPE_ISDN:
    case DNS_TYPE_TEXT:
    case DNS_TYPE_X25:
    {
        for (i = 0; i < src->Data.TXT.dwStringCount; i++)
        {
            dst->Data.TXT.pStringArray[i] =
                dns_strcpyX( src->Data.TXT.pStringArray[i], in, out );

            if (!dst->Data.TXT.pStringArray[i])
            {
                while (i > 0) dns_free( dst->Data.TXT.pStringArray[--i] );
                goto error;
            }
        }
        break;
    }
    case DNS_TYPE_MINFO:
    case DNS_TYPE_RP:
    {
        dst->Data.MINFO.pNameMailbox =
            dns_strcpyX( src->Data.MINFO.pNameMailbox, in, out );
        if (!dst->Data.MINFO.pNameMailbox) goto error;

        dst->Data.MINFO.pNameErrorsMailbox =
            dns_strcpyX( src->Data.MINFO.pNameErrorsMailbox, in, out );
        if (!dst->Data.MINFO.pNameErrorsMailbox)
        {
            dns_free( dst->Data.MINFO.pNameMailbox );
            goto error;
        }
        break;
    }
    case DNS_TYPE_AFSDB:
    case DNS_TYPE_RT:
    case DNS_TYPE_MX:
    {
        dst->Data.MX.pNameExchange =
            dns_strcpyX( src->Data.MX.pNameExchange, in, out );
        if (!dst->Data.MX.pNameExchange) goto error;
        break;
    }
    case DNS_TYPE_NXT:
    {
        dst->Data.NXT.pNameNext =
            dns_strcpyX( src->Data.NXT.pNameNext, in, out );
        if (!dst->Data.NXT.pNameNext) goto error;
        break;
    }
    case DNS_TYPE_CNAME:
    case DNS_TYPE_MB:
    case DNS_TYPE_MD:
    case DNS_TYPE_MF:
    case DNS_TYPE_MG:
    case DNS_TYPE_MR:
    case DNS_TYPE_NS:
    case DNS_TYPE_PTR:
    {
        dst->Data.PTR.pNameHost =
            dns_strcpyX( src->Data.PTR.pNameHost, in, out );
        if (!dst->Data.PTR.pNameHost) goto error;
        break;
    }
    case DNS_TYPE_SIG:
    {
        dst->Data.SIG.pNameSigner =
            dns_strcpyX( src->Data.SIG.pNameSigner, in, out );
        if (!dst->Data.SIG.pNameSigner) goto error;
        break;
    }
    case DNS_TYPE_SOA:
    {
        dst->Data.SOA.pNamePrimaryServer =
            dns_strcpyX( src->Data.SOA.pNamePrimaryServer, in, out );
        if (!dst->Data.SOA.pNamePrimaryServer) goto error;

        dst->Data.SOA.pNameAdministrator =
            dns_strcpyX( src->Data.SOA.pNameAdministrator, in, out );
        if (!dst->Data.SOA.pNameAdministrator)
        {
            dns_free( dst->Data.SOA.pNamePrimaryServer );
            goto error;
        }
        break;
    }
    case DNS_TYPE_SRV:
    {
        dst->Data.SRV.pNameTarget =
            dns_strcpyX( src->Data.SRV.pNameTarget, in, out );
        if (!dst->Data.SRV.pNameTarget) goto error;
        break;
    }
    default:
        break;
    }
    return dst;

error:
    dns_free( dst->pName );
    dns_free( dst );
    return NULL;
}

/******************************************************************************
 * DnsRecordListFree                       [DNSAPI.@]
 *
 */
VOID WINAPI DnsRecordListFree( PDNS_RECORD list, DNS_FREE_TYPE type )
{
    DNS_RECORD *r, *next;
    unsigned int i;

    TRACE( "(%p,%d)\n", list, type );

    if (!list) return;

    switch (type)
    {
    case DnsFreeRecordList:
    {
        for (r = list; (list = r); r = next)
        {
            dns_free( r->pName );

            switch (r->wType)
            {
            case DNS_TYPE_HINFO:
            case DNS_TYPE_ISDN:
            case DNS_TYPE_TEXT:
            case DNS_TYPE_X25:
            {
                for (i = 0; i < r->Data.TXT.dwStringCount; i++)
                    dns_free( r->Data.TXT.pStringArray[i] );

                break;
            }
            case DNS_TYPE_MINFO:
            case DNS_TYPE_RP:
            {
                dns_free( r->Data.MINFO.pNameMailbox );
                dns_free( r->Data.MINFO.pNameErrorsMailbox );
                break;
            }
            case DNS_TYPE_AFSDB:
            case DNS_TYPE_RT:
            case DNS_TYPE_MX:
            {
                dns_free( r->Data.MX.pNameExchange );
                break;
            }
            case DNS_TYPE_NXT:
            {
                dns_free( r->Data.NXT.pNameNext );
                break;
            }
            case DNS_TYPE_CNAME:
            case DNS_TYPE_MB:
            case DNS_TYPE_MD:
            case DNS_TYPE_MF:
            case DNS_TYPE_MG:
            case DNS_TYPE_MR:
            case DNS_TYPE_NS:
            case DNS_TYPE_PTR:
            {
                dns_free( r->Data.PTR.pNameHost );
                break;
            }
            case DNS_TYPE_SIG:
            {
                dns_free( r->Data.SIG.pNameSigner );
                break;
            }
            case DNS_TYPE_SOA:
            {
                dns_free( r->Data.SOA.pNamePrimaryServer );
                dns_free( r->Data.SOA.pNameAdministrator );
                break;
            }
            case DNS_TYPE_SRV:
            {
                dns_free( r->Data.SRV.pNameTarget );
                break;
            }
            default:
                break;
            }

            next = r->pNext;
            dns_free( r );
        }
        break;
    }
    case DnsFreeFlat:
    case DnsFreeParsedMessageFields:
    {
        FIXME( "unhandled free type: %d\n", type );
        break;
    }
    default:
        WARN( "unknown free type: %d\n", type );
        break;
    }
}

/******************************************************************************
 * DnsRecordSetCompare                     [DNSAPI.@]
 *
 */
BOOL WINAPI DnsRecordSetCompare( PDNS_RECORD set1, PDNS_RECORD set2,
                                 PDNS_RECORD *diff1, PDNS_RECORD *diff2 )
{
    BOOL ret = TRUE;
    DNS_RECORD *r, *s1, *s2, *t, *u;
    DNS_RRSET rr1, rr2;

    TRACE( "(%p,%p,%p,%p)\n", set1, set2, diff1, diff2 );

    if (!set1 && !set2) return FALSE;

    if (diff1) *diff1 = NULL;
    if (diff2) *diff2 = NULL;

    if (set1 && !set2)
    {
        if (diff1) *diff1 = DnsRecordSetCopyEx( set1, 0, set1->Flags.S.CharSet );
        return FALSE;
    }
    if (!set1 && set2)
    {
        if (diff2) *diff2 = DnsRecordSetCopyEx( set2, 0, set2->Flags.S.CharSet );
        return FALSE;
    }

    DNS_RRSET_INIT( rr1 );
    DNS_RRSET_INIT( rr2 );

    for (r = s1 = set1; (s1 = r); r = r->pNext)
    {
        for (t = s2 = set2; (s2 = t); t = t->pNext)
        {
            u = DnsRecordCopyEx( r, r->Flags.S.CharSet, t->Flags.S.CharSet );
            if (!u) goto error;

            if (!DnsRecordCompare( t, u ))
            {
                DNS_RRSET_ADD( rr1, u );
                ret = FALSE;
            }
            else dns_free( u );
        }
    }

    for (t = s2 = set2; (s2 = t); t = t->pNext)
    {
        for (r = s1 = set1; (s1 = r); r = r->pNext)
        {
            u = DnsRecordCopyEx( t, t->Flags.S.CharSet, r->Flags.S.CharSet );
            if (!u) goto error;

            if (!DnsRecordCompare( r, u ))
            {
                DNS_RRSET_ADD( rr2, u );
                ret = FALSE;
            }
            else dns_free( u );
        }
    }

    DNS_RRSET_TERMINATE( rr1 );
    DNS_RRSET_TERMINATE( rr2 );
    
    if (diff1) *diff1 = rr1.pFirstRR;
    if (diff2) *diff2 = rr2.pFirstRR;

    return ret;

error:
    DNS_RRSET_TERMINATE( rr1 );
    DNS_RRSET_TERMINATE( rr2 );

    DnsRecordListFree( rr1.pFirstRR, DnsFreeRecordList );
    DnsRecordListFree( rr2.pFirstRR, DnsFreeRecordList );
    
    return FALSE;
}

/******************************************************************************
 * DnsRecordSetCopyEx                      [DNSAPI.@]
 *
 */
PDNS_RECORD WINAPI DnsRecordSetCopyEx( PDNS_RECORD src_set, DNS_CHARSET in, DNS_CHARSET out )
{
    DNS_RRSET dst_set;
    DNS_RECORD *src, *dst;

    TRACE( "(%p,%d,%d)\n", src_set, in, out );

    DNS_RRSET_INIT( dst_set );

    for (src = src_set; (src_set = src); src = src_set->pNext)
    {
        dst = DnsRecordCopyEx( src, in, out );
        if (!dst)
        {
            DNS_RRSET_TERMINATE( dst_set );
            DnsRecordListFree( dst_set.pFirstRR, DnsFreeRecordList );
            return NULL;
        }
        DNS_RRSET_ADD( dst_set, dst );
    }

    DNS_RRSET_TERMINATE( dst_set );
    return dst_set.pFirstRR;
}

/******************************************************************************
 * DnsRecordSetDetach                      [DNSAPI.@]
 *
 */
PDNS_RECORD WINAPI DnsRecordSetDetach( PDNS_RECORD set )
{
    DNS_RECORD *r, *s;

    TRACE( "(%p)\n", set );

    for (r = set; (set = r); r = set->pNext)
    {
        if (r->pNext && !r->pNext->pNext)
        {
            s = r->pNext;
            r->pNext = NULL;
            return s;
        }
    }
    return NULL;
}