Resolve chained CNAME records
[reactos.git] / reactos / lib / dnsapi / dnsapi / query.c
index 8271903..d8b7fae 100644 (file)
@@ -8,11 +8,10 @@
  *              12/15/03 -- Created
  */
 
-#include <windows.h>
-#include <WinError.h>
-#include <WinDNS.h>
-#include <internal/windns.h>
-#include <string.h>
+#include "precomp.h"
+
+#define NDEBUG
+#include <debug.h>
 
 /* DnsQuery ****************************
  * Begin a DNS query, and allow the result to be placed in the application
 char *xstrsave(const char *str) {
   char *p;
   
-  p= RtlAllocateHeap( RtlGetProcessHeap(), 0, strlen(str)+1 );
-  strcpy(p,str);
+  p = RtlAllocateHeap( RtlGetProcessHeap(), 0, strlen(str)+1 );
+  if ( NULL != p ) {
+    strcpy(p,str);
+  }
   return p;
 }
 
@@ -45,12 +46,14 @@ DNS_STATUS WINAPI DnsQuery_A
   WORD Type,
   DWORD Options,
   PIP4_ARRAY Servers,
-  PDNS_RECORDA *QueryResultSet,
+  PDNS_RECORD *QueryResultSet,
   PVOID *Reserved ) {
   adns_state astate;
   int quflags = 0;
   int adns_error;
   adns_answer *answer;
+  LPSTR CurrentName;
+  unsigned CNameLoop;
 
   *QueryResultSet = 0;
 
@@ -62,33 +65,83 @@ DNS_STATUS WINAPI DnsQuery_A
                            adns_if_noserverwarn,
                            0 );
     if( adns_error != adns_s_ok ) {
-      adns_finish( astate );
       return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
     }
 
-    adns_error = adns_synchronous( astate, 
-                                  Name, 
-                                  adns_r_addr, 
-                                  quflags, 
-                                  &answer );
+    /*
+     * adns doesn't resolve chained CNAME records (a CNAME which points to
+     * another CNAME pointing to another... pointing to an A record), according
+     * to a mailing list thread the authors believe that chained CNAME records
+     * are invalid and the DNS entries should be fixed. That's a nice academic
+     * standpoint, but there certainly are chained CNAME records out there,
+     * even some fairly major ones (at the time of this writing
+     * download.mozilla.org is a chained CNAME). Everyone else seems to resolve
+     * these fine, so we should too. So we loop here to try to resolve CNAME
+     * chains ourselves. Of course, there must be a limit to protect against
+     * CNAME loops.
+     */
+
+#define CNAME_LOOP_MAX 16
+
+    CurrentName = (LPSTR) Name;
+    for ( CNameLoop = 0; CNameLoop < CNAME_LOOP_MAX; CNameLoop++ ) {
+      adns_error = adns_synchronous( astate, 
+                                     CurrentName, 
+                                     adns_r_addr, 
+                                     quflags, 
+                                     &answer );
                                   
-    if( adns_error != adns_s_ok ) {
-      adns_finish( astate );
-      return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
-    }
+      if( adns_error != adns_s_ok ) {
+        adns_finish( astate );
+        if ( CurrentName != Name ) {
+          RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
+        }
+        return DnsIntTranslateAdnsToDNS_STATUS( adns_error );
+      }
 
-    *QueryResultSet = (PDNS_RECORDA)RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                                    sizeof( DNS_RECORDA ) );
-    (*QueryResultSet)->wType = Type;
-    (*QueryResultSet)->pName = xstrsave( Name );
-    (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
-    (*QueryResultSet)->Data.A.IpAddress = 
-      ntohl( answer->rrs.addr->addr.inet.sin_addr.s_addr );
+      if( answer && answer->rrs.addr ) {
+        if ( CurrentName != Name ) {
+          RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
+        }
+        *QueryResultSet = 
+          (PDNS_RECORD)RtlAllocateHeap( RtlGetProcessHeap(), 0,
+                                        sizeof( DNS_RECORD ) );
+        if ( NULL == *QueryResultSet ) {
+          adns_finish( astate );
+          return ERROR_OUTOFMEMORY;
+        }
+        (*QueryResultSet)->pNext = NULL;
+        (*QueryResultSet)->wType = Type;
+        (*QueryResultSet)->wDataLength = sizeof(DNS_A_DATA);
+        (*QueryResultSet)->Data.A.IpAddress = 
+            answer->rrs.addr->addr.inet.sin_addr.s_addr;
+        adns_finish( astate );
+        (*QueryResultSet)->pName = xstrsave( Name );
+        return NULL != (*QueryResultSet)->pName ? ERROR_SUCCESS :
+                                                  ERROR_OUTOFMEMORY;
+      }
+      if ( NULL == answer || adns_s_prohibitedcname != answer->status ||
+           NULL == answer->cname ) {
+        adns_finish( astate );
+        if ( CurrentName != Name ) {
+          RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
+        }
+        return ERROR_FILE_NOT_FOUND;
+      }
+      if ( CurrentName != Name ) {
+        RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
+      }
+      CurrentName = xstrsave( answer->cname );
+      if ( NULL == CurrentName ) {
+        adns_finish( astate );
+        return ERROR_OUTOFMEMORY;
+      }
+    }
     adns_finish( astate );
-    return ERROR_SUCCESS;
-    
+    RtlFreeHeap( CurrentName, 0, GetProcessHeap() );
+    return ERROR_FILE_NOT_FOUND;
   default:
-    return DNS_ERROR_NO_MEMORY; /* XXX arty: find a better error code. */
+    return ERROR_OUTOFMEMORY; /* XXX arty: find a better error code. */
   }
 }
 
@@ -112,13 +165,13 @@ DNS_STATUS WINAPI DnsQuery_W
   WORD Type,
   DWORD Options,
   PIP4_ARRAY Servers,
-  PDNS_RECORDW *QueryResultSet,
+  PDNS_RECORD *QueryResultSet,
   PVOID *Reserved ) {
-  int i;
+  UINT i;
   PCHAR Buffer;
   DNS_STATUS Status;
-  PDNS_RECORDA QueryResultWide;
-  PDNS_RECORDW ConvertedRecord = 0, LastRecord = 0;
+  PDNS_RECORD QueryResultWide;
+  PDNS_RECORD ConvertedRecord = 0, LastRecord = 0;
 
   Buffer = DnsWToC( Name );
 
@@ -129,11 +182,13 @@ DNS_STATUS WINAPI DnsQuery_W
     switch( QueryResultWide->wType ) {
     case DNS_TYPE_A:
     case DNS_TYPE_WKS:
+#ifndef __USE_W32API
     case DNS_TYPE_AAAA:
     case DNS_TYPE_KEY:
+#endif
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                        sizeof(DNS_RECORDA) );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+                                        sizeof(DNS_RECORD) );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
       ConvertedRecord->wDataLength = QueryResultWide->wDataLength;
       memcpy( ConvertedRecord, QueryResultWide, 
@@ -149,57 +204,63 @@ DNS_STATUS WINAPI DnsQuery_W
     case DNS_TYPE_MG:
     case DNS_TYPE_MR:
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                        sizeof(DNS_RECORDA) );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+                                        sizeof(DNS_RECORD) );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
-      ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATAA);
+      ConvertedRecord->wDataLength = sizeof(DNS_PTR_DATA);
       ConvertedRecord->Data.PTR.pNameHost = 
-       DnsCToW( QueryResultWide->Data.PTR.pNameHost );
+         (PCHAR)DnsCToW( QueryResultWide->Data.PTR.pNameHost );
       break;
     case DNS_TYPE_MINFO:
+#ifndef __USE_W32API
     case DNS_TYPE_RP:
+#endif
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                        sizeof(DNS_RECORDA) );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+                                        sizeof(DNS_RECORD) );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
-      ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATAA);
+      ConvertedRecord->wDataLength = sizeof(DNS_MINFO_DATA);
       ConvertedRecord->Data.MINFO.pNameMailbox =
-       DnsCToW( QueryResultWide->Data.MINFO.pNameMailbox );
+         (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameMailbox );
       ConvertedRecord->Data.MINFO.pNameErrorsMailbox =
-       DnsCToW( QueryResultWide->Data.MINFO.pNameErrorsMailbox );
+         (PCHAR)DnsCToW( QueryResultWide->Data.MINFO.pNameErrorsMailbox );
       break;
 
     case DNS_TYPE_MX:
+#ifndef __USE_W32API
     case DNS_TYPE_AFSDB:
     case DNS_TYPE_RT:
+#endif
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                        sizeof(DNS_RECORDA) );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+                                        sizeof(DNS_RECORD) );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
-      ConvertedRecord->wDataLength = sizeof(DNS_MX_DATAW);
+      ConvertedRecord->wDataLength = sizeof(DNS_MX_DATA);
       ConvertedRecord->Data.MX.pNameExchange = 
-       DnsCToW( QueryResultWide->Data.MX.pNameExchange );
+         (PCHAR)DnsCToW( QueryResultWide->Data.MX.pNameExchange );
       ConvertedRecord->Data.MX.wPreference =
-       QueryResultWide->Data.MX.wPreference;
+         QueryResultWide->Data.MX.wPreference;
       break;
 
+#ifndef __USE_W32API
     case DNS_TYPE_TXT:
-    case DNS_TYPE_HINFO:
     case DNS_TYPE_ISDN:
+#endif
+    case DNS_TYPE_HINFO:
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
-                                        sizeof(DNS_TXT_DATAW) + 
+                                        sizeof(DNS_TXT_DATA) + 
                                         QueryResultWide->
                                         Data.TXT.dwStringCount );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
       ConvertedRecord->wDataLength = 
-       sizeof(DNS_TXT_DATAW) + 
+       sizeof(DNS_TXT_DATA) + 
        (sizeof(PWCHAR) * QueryResultWide->Data.TXT.dwStringCount);
       ConvertedRecord->Data.TXT.dwStringCount = 
        QueryResultWide->Data.TXT.dwStringCount;
       for( i = 0; i < ConvertedRecord->Data.TXT.dwStringCount; i++ ) {
        ConvertedRecord->Data.TXT.pStringArray[i] = 
-         DnsCToW( QueryResultWide->Data.TXT.pStringArray[i] );
+           (PCHAR)DnsCToW( QueryResultWide->Data.TXT.pStringArray[i] );
       }
       break;
 
@@ -208,7 +269,7 @@ DNS_STATUS WINAPI DnsQuery_W
                                         sizeof(DNS_NULL_DATA) + 
                                         QueryResultWide->
                                         Data.Null.dwByteCount );
-      ConvertedRecord->pName = DnsCToW( QueryResultWide->pName );
+      ConvertedRecord->pName = (PCHAR)DnsCToW( QueryResultWide->pName );
       ConvertedRecord->wType = QueryResultWide->wType;
       ConvertedRecord->wDataLength =
        sizeof(DNS_NULL_DATA) + QueryResultWide->Data.Null.dwByteCount;
@@ -219,6 +280,7 @@ DNS_STATUS WINAPI DnsQuery_W
              QueryResultWide->Data.Null.dwByteCount );
       break;
 
+#ifndef __USE_W32API
     case DNS_TYPE_SIG:
       ConvertedRecord = RtlAllocateHeap( RtlGetProcessHeap(), 0, 
                                         sizeof(DNS_RECORDA) );
@@ -257,6 +319,7 @@ DNS_STATUS WINAPI DnsQuery_W
       ConvertedRecord->Data.SRV.pNameTarget =
        DnsCToW( QueryResultWide->Data.SRV.pNameTarget );
       break;
+#endif
     }
 
     if( LastRecord ) {
@@ -280,69 +343,75 @@ DNS_STATUS WINAPI DnsQuery_UTF8
   WORD Type,
   DWORD Options,
   PIP4_ARRAY Servers,
-  PDNS_RECORDA *QueryResultSet,
+  PDNS_RECORD *QueryResultSet,
   PVOID *Reserved ) {
   return DnsQuery_UTF8( Name, Type, Options, Servers, QueryResultSet, 
                        Reserved );
 }
 
-void DnsIntFreeRecordList( PDNS_RECORDA ToDelete ) {
-  int i;
-  PDNS_RECORDA next = 0;
+void DnsIntFreeRecordList( PDNS_RECORD ToDelete ) {
+  UINT i;
+  PDNS_RECORD next = 0;
 
   while( ToDelete ) {
-    if( ToDelete->pName ) 
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->pName );
-    switch( ToDelete->wType ) {
-    case DNS_TYPE_CNAME:
-    case DNS_TYPE_PTR:
-    case DNS_TYPE_NS:
-    case DNS_TYPE_MB:
-    case DNS_TYPE_MD:
-    case DNS_TYPE_MF:
-    case DNS_TYPE_MG:
-    case DNS_TYPE_MR:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost );
-      break;
-    case DNS_TYPE_MINFO:
-    case DNS_TYPE_RP:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, 
-                  ToDelete->Data.MINFO.pNameMailbox );
-      RtlFreeHeap( RtlGetProcessHeap(), 0,
-                  ToDelete->Data.MINFO.pNameErrorsMailbox );
-      break;
-
-    case DNS_TYPE_MX:
-    case DNS_TYPE_AFSDB:
-    case DNS_TYPE_RT:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange );
-      break;
-
-    case DNS_TYPE_TXT:
-    case DNS_TYPE_HINFO:
-    case DNS_TYPE_ISDN:
-      for( i = 0; i < ToDelete->Data.TXT.dwStringCount; i++ ) {
-       RtlFreeHeap( RtlGetProcessHeap(), 0, 
-                    ToDelete->Data.TXT.pStringArray[i] );
+      if( ToDelete->pName ) 
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->pName );
+      switch( ToDelete->wType ) {
+      case DNS_TYPE_CNAME:
+      case DNS_TYPE_PTR:
+      case DNS_TYPE_NS:
+      case DNS_TYPE_MB:
+      case DNS_TYPE_MD:
+      case DNS_TYPE_MF:
+      case DNS_TYPE_MG:
+      case DNS_TYPE_MR:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.PTR.pNameHost );
+         break;
+      case DNS_TYPE_MINFO:
+#ifndef __USE_W32API
+      case DNS_TYPE_RP:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, 
+                      ToDelete->Data.MINFO.pNameMailbox );
+         RtlFreeHeap( RtlGetProcessHeap(), 0,
+                      ToDelete->Data.MINFO.pNameErrorsMailbox );
+         break;
+         
+      case DNS_TYPE_AFSDB:
+      case DNS_TYPE_RT:
+#endif
+      case DNS_TYPE_MX:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.MX.pNameExchange );
+         break;
+         
+#ifndef __USE_W32API
+      case DNS_TYPE_TXT:
+      case DNS_TYPE_ISDN:
+#endif
+      case DNS_TYPE_HINFO:
+         for( i = 0; i < ToDelete->Data.TXT.dwStringCount; i++ ) {
+             RtlFreeHeap( RtlGetProcessHeap(), 0, 
+                          ToDelete->Data.TXT.pStringArray[i] );
+         }
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray );
+         break;
+         
+#ifndef __USE_W32API
+      case DNS_TYPE_SIG:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SIG.pNameSigner );
+         break;
+         
+      case DNS_TYPE_NXT:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.NXT.pNameNext );
+         break;
+         
+      case DNS_TYPE_SRV:
+         RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SRV.pNameTarget );
+         break;
+#endif
       }
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.TXT.pStringArray );
-      break;
-
-    case DNS_TYPE_SIG:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SIG.pNameSigner );
-      break;
-
-    case DNS_TYPE_NXT:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.NXT.pNameNext );
-      break;
-
-    case DNS_TYPE_SRV:
-      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete->Data.SRV.pNameTarget );
-      break;
-    }
-
-    next = ToDelete->pNext;
-    RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete );
-    ToDelete = next;
+      
+      next = ToDelete->pNext;
+      RtlFreeHeap( RtlGetProcessHeap(), 0, ToDelete );
+      ToDelete = next;
   }
 }