/* * 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 "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 = heap_alloc_zero( 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) heap_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) { heap_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) { heap_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: heap_free( dst->pName ); heap_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) { heap_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++) heap_free( r->Data.TXT.pStringArray[i] ); break; } case DNS_TYPE_MINFO: case DNS_TYPE_RP: { heap_free( r->Data.MINFO.pNameMailbox ); heap_free( r->Data.MINFO.pNameErrorsMailbox ); break; } case DNS_TYPE_AFSDB: case DNS_TYPE_RT: case DNS_TYPE_MX: { heap_free( r->Data.MX.pNameExchange ); break; } case DNS_TYPE_NXT: { heap_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: { heap_free( r->Data.PTR.pNameHost ); break; } case DNS_TYPE_SIG: { heap_free( r->Data.SIG.pNameSigner ); break; } case DNS_TYPE_SOA: { heap_free( r->Data.SOA.pNamePrimaryServer ); heap_free( r->Data.SOA.pNameAdministrator ); break; } case DNS_TYPE_SRV: { heap_free( r->Data.SRV.pNameTarget ); break; } default: break; } next = r->pNext; heap_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, *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 = set1; r; r = r->pNext) { for (t = set2; 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 DnsRecordListFree( u, DnsFreeRecordList ); } } for (t = set2; t; t = t->pNext) { for (r = set1; 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 DnsRecordListFree( u, DnsFreeRecordList ); } } DNS_RRSET_TERMINATE( rr1 ); DNS_RRSET_TERMINATE( rr2 ); if (diff1) *diff1 = rr1.pFirstRR; else DnsRecordListFree( rr1.pFirstRR, DnsFreeRecordList ); if (diff2) *diff2 = rr2.pFirstRR; else DnsRecordListFree( rr2.pFirstRR, DnsFreeRecordList ); 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; }