dnsapi: Correct testing for zero option value DNS_QUERY_STANDARD.
[wine] / dlls / dnsapi / query.c
1 /*
2  * DNS support
3  *
4  * Copyright (C) 2006 Hans Leidekker
5  * 
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23 #include "wine/debug.h"
24
25 #include <stdarg.h>
26 #include <string.h>
27 #include <sys/types.h>
28
29 #ifdef HAVE_NETINET_IN_H
30 # include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_NAMESER_H
33 # include <arpa/nameser.h>
34 #endif
35 #ifdef HAVE_RESOLV_H
36 # include <resolv.h>
37 #endif
38 #ifdef HAVE_NETDB_H
39 # include <netdb.h>
40 #endif
41
42 #include "windef.h"
43 #include "winbase.h"
44 #include "winerror.h"
45 #include "winnls.h"
46 #include "windns.h"
47 #include "nb30.h"
48
49 #include "dnsapi.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(dnsapi);
52
53 #ifdef HAVE_RESOLV
54
55 static CRITICAL_SECTION resolver_cs;
56 static CRITICAL_SECTION_DEBUG resolver_cs_debug =
57 {
58     0, 0, &resolver_cs,
59     { &resolver_cs_debug.ProcessLocksList,
60       &resolver_cs_debug.ProcessLocksList },
61       0, 0, { (DWORD_PTR)(__FILE__ ": resolver_cs") }
62 };
63 static CRITICAL_SECTION resolver_cs = { &resolver_cs_debug, -1, 0, 0, 0, 0 };
64
65 #define LOCK_RESOLVER()     do { EnterCriticalSection( &resolver_cs ); } while (0)
66 #define UNLOCK_RESOLVER()   do { LeaveCriticalSection( &resolver_cs ); } while (0)
67
68
69 static const char *dns_section_to_str( ns_sect section )
70 {
71     switch (section)
72     {
73     case ns_s_qd:   return "Question";
74     case ns_s_an:   return "Answer";
75     case ns_s_ns:   return "Authority";
76     case ns_s_ar:   return "Additional";
77     default:
78     {
79         static char tmp[5];
80         FIXME( "unknown section: 0x%02x\n", section );
81         sprintf( tmp, "0x%02x", section );
82         return tmp;
83     }
84     }
85 }
86
87 static unsigned long dns_map_options( DWORD options )
88 {
89     unsigned long ret = 0;
90             
91     if (options == DNS_QUERY_STANDARD)
92         return RES_DEFAULT;
93
94     if (options & DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE)
95         ret |= RES_IGNTC;
96     if (options & DNS_QUERY_USE_TCP_ONLY)
97         ret |= RES_USEVC;
98     if (options & DNS_QUERY_NO_RECURSION)
99         ret &= ~RES_RECURSE;
100     if (options & DNS_QUERY_NO_LOCAL_NAME)
101         ret &= ~RES_DNSRCH;
102     if (options & DNS_QUERY_NO_HOSTS_FILE)
103         ret |= RES_NOALIASES;
104     if (options & DNS_QUERY_TREAT_AS_FQDN)
105         ret &= ~RES_DEFNAMES;
106
107     if (options & DNS_QUERY_DONT_RESET_TTL_VALUES)
108         FIXME( "option DNS_QUERY_DONT_RESET_TTL_VALUES not implemented\n" );
109     if (options & DNS_QUERY_RESERVED)
110         FIXME( "option DNS_QUERY_RESERVED not implemented\n" );
111     if (options & DNS_QUERY_WIRE_ONLY)
112         FIXME( "option DNS_QUERY_WIRE_ONLY not implemented\n" );
113     if (options & DNS_QUERY_NO_WIRE_QUERY)
114         FIXME( "option DNS_QUERY_NO_WIRE_QUERY not implemented\n" );
115     if (options & DNS_QUERY_BYPASS_CACHE)
116         FIXME( "option DNS_QUERY_BYPASS_CACHE not implemented\n" );
117     if (options & DNS_QUERY_RETURN_MESSAGE)
118         FIXME( "option DNS_QUERY_RETURN_MESSAGE not implemented\n" );
119
120     if (options & DNS_QUERY_NO_NETBT)
121         TRACE( "netbios query disabled\n" );
122
123     return ret;
124 }
125
126 static DNS_STATUS dns_map_error( int error )
127 {
128     switch (error)
129     {
130     case ns_r_noerror:  return ERROR_SUCCESS;
131     case ns_r_formerr:  return DNS_ERROR_RCODE_FORMAT_ERROR;
132     case ns_r_servfail: return DNS_ERROR_RCODE_SERVER_FAILURE;
133     case ns_r_nxdomain: return DNS_ERROR_RCODE_NAME_ERROR;
134     case ns_r_notimpl:  return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
135     case ns_r_refused:  return DNS_ERROR_RCODE_REFUSED;
136     case ns_r_yxdomain: return DNS_ERROR_RCODE_YXDOMAIN;
137     case ns_r_yxrrset:  return DNS_ERROR_RCODE_YXRRSET;
138     case ns_r_nxrrset:  return DNS_ERROR_RCODE_NXRRSET;
139     case ns_r_notauth:  return DNS_ERROR_RCODE_NOTAUTH;
140     case ns_r_notzone:  return DNS_ERROR_RCODE_NOTZONE;
141     default:
142         FIXME( "unmapped error code: %d\n", error );
143         return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
144     }
145 }
146
147 static DNS_STATUS dns_map_h_errno( int error )
148 {
149     switch (error)
150     {
151     case NO_DATA:
152     case HOST_NOT_FOUND: return DNS_ERROR_RCODE_NAME_ERROR;
153     case TRY_AGAIN:      return DNS_ERROR_RCODE_SERVER_FAILURE;
154     case NO_RECOVERY:    return DNS_ERROR_RCODE_REFUSED;
155     case NETDB_INTERNAL: return DNS_ERROR_RCODE;
156     default:
157         FIXME( "unmapped error code: %d\n", error );
158         return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
159     }
160 }
161
162 static char *dns_dname_from_msg( ns_msg msg, const unsigned char *pos )
163 {
164     int len;
165     char *str, dname[NS_MAXDNAME] = ".";
166
167     /* returns *compressed* length, ignore it */
168     len = dns_ns_name_uncompress( ns_msg_base( msg ), ns_msg_end( msg ),
169                                   pos, dname, sizeof(dname) );
170
171     len = strlen( dname );
172     str = dns_alloc( len + 1 );
173     if (str) strcpy( str, dname );
174     return str;
175 }
176
177 static char *dns_str_from_rdata( const unsigned char *rdata )
178 {
179     char *str;
180     unsigned int len = rdata[0];
181
182     str = dns_alloc( len + 1 );
183     if (str)
184     {
185         memcpy( str, ++rdata, len );
186         str[len] = '\0';
187     }
188     return str;
189 }
190
191 static unsigned int dns_get_record_size( ns_rr *rr )
192 {
193     const unsigned char *pos = rr->rdata;
194     unsigned int num = 0, size = sizeof(DNS_RECORDA);
195
196     switch (rr->type)
197     {
198     case ns_t_key:
199     {
200         pos += sizeof(WORD) + sizeof(BYTE) + sizeof(BYTE);
201         size += rr->rdata + rr->rdlength - pos - 1;
202         break;
203     }
204     case ns_t_sig:
205     {
206         pos += sizeof(PCHAR) + sizeof(WORD) + 2 * sizeof(BYTE);
207         pos += 3 * sizeof(DWORD) + 2 * sizeof(WORD);
208         size += rr->rdata + rr->rdlength - pos - 1;
209         break;
210     }
211     case ns_t_hinfo:
212     case ns_t_isdn:
213     case ns_t_txt:
214     case ns_t_x25:
215     {
216         while (pos[0] && pos < rr->rdata + rr->rdlength)
217         {
218             num++;
219             pos += pos[0] + 1;
220         }
221         size += (num - 1) * sizeof(PCHAR);
222         break;
223     }
224     case ns_t_null:
225     {
226         size += rr->rdlength - 1;
227         break;
228     }
229     case ns_t_nxt:
230     case ns_t_wks:
231     case 0xff01:  /* WINS */
232     {
233         FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
234         break;
235     }
236     default:
237         break;
238     }
239     return size;
240 }
241
242 static DNS_STATUS dns_copy_rdata( ns_msg msg, ns_rr *rr, DNS_RECORDA *r, WORD *dlen )
243 {
244     DNS_STATUS ret = ERROR_SUCCESS;
245     const unsigned char *pos = rr->rdata;
246     unsigned int i, size;
247
248     switch (rr->type)
249     {
250     case ns_t_a:
251     {
252         r->Data.A.IpAddress = *(DWORD *)pos;
253         *dlen = sizeof(DNS_A_DATA);
254         break; 
255     }
256     case ns_t_aaaa:
257     {
258         for (i = 0; i < sizeof(IP6_ADDRESS)/sizeof(DWORD); i++)
259         {
260             r->Data.AAAA.Ip6Address.IP6Dword[i] = *(DWORD *)pos;
261             pos += sizeof(DWORD);
262         }
263
264         *dlen = sizeof(DNS_AAAA_DATA);
265         break;
266     }
267     case ns_t_key:
268     {
269         /* FIXME: byte order? */
270         r->Data.KEY.wFlags      = *(WORD *)pos;   pos += sizeof(WORD);
271         r->Data.KEY.chProtocol  = *(BYTE *)pos++;
272         r->Data.KEY.chAlgorithm = *(BYTE *)pos++;
273
274         size = rr->rdata + rr->rdlength - pos;
275
276         for (i = 0; i < size; i++)
277             r->Data.KEY.Key[i] = *(BYTE *)pos++;
278
279         *dlen = sizeof(DNS_KEY_DATA) + (size - 1) * sizeof(BYTE);
280         break;
281     }
282     case ns_t_rp:
283     case ns_t_minfo:
284     {
285         r->Data.MINFO.pNameMailbox = dns_dname_from_msg( msg, pos );
286         if (!r->Data.MINFO.pNameMailbox) return ERROR_NOT_ENOUGH_MEMORY;
287
288         if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
289             return DNS_ERROR_BAD_PACKET;
290
291         r->Data.MINFO.pNameErrorsMailbox = dns_dname_from_msg( msg, pos );
292         if (!r->Data.MINFO.pNameErrorsMailbox)
293         {
294             dns_free( r->Data.MINFO.pNameMailbox ); 
295             return ERROR_NOT_ENOUGH_MEMORY;
296         }
297
298         *dlen = sizeof(DNS_MINFO_DATAA);
299         break; 
300     }
301     case ns_t_afsdb:
302     case ns_t_rt:
303     case ns_t_mx:
304     {
305         r->Data.MX.wPreference = ntohs( *(WORD *)pos );
306         r->Data.MX.pNameExchange = dns_dname_from_msg( msg, pos + sizeof(WORD) );
307         if (!r->Data.MX.pNameExchange) return ERROR_NOT_ENOUGH_MEMORY;
308
309         *dlen = sizeof(DNS_MX_DATAA);
310         break; 
311     }
312     case ns_t_null:
313     {
314         r->Data.Null.dwByteCount = rr->rdlength;
315         memcpy( r->Data.Null.Data, rr->rdata, rr->rdlength );
316
317         *dlen = sizeof(DNS_NULL_DATA) + rr->rdlength - 1;
318         break;
319     }
320     case ns_t_cname:
321     case ns_t_ns:
322     case ns_t_mb:
323     case ns_t_md:
324     case ns_t_mf:
325     case ns_t_mg:
326     case ns_t_mr:
327     case ns_t_ptr:
328     {
329         r->Data.PTR.pNameHost = dns_dname_from_msg( msg, pos );
330         if (!r->Data.PTR.pNameHost) return ERROR_NOT_ENOUGH_MEMORY;
331
332         *dlen = sizeof(DNS_PTR_DATAA);
333         break;
334     }
335     case ns_t_sig:
336     {
337         r->Data.SIG.pNameSigner = dns_dname_from_msg( msg, pos );
338         if (!r->Data.SIG.pNameSigner) return ERROR_NOT_ENOUGH_MEMORY;
339
340         if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
341             return DNS_ERROR_BAD_PACKET;
342
343         /* FIXME: byte order? */
344         r->Data.SIG.wTypeCovered  = *(WORD *)pos;   pos += sizeof(WORD);
345         r->Data.SIG.chAlgorithm   = *(BYTE *)pos++;
346         r->Data.SIG.chLabelCount  = *(BYTE *)pos++;
347         r->Data.SIG.dwOriginalTtl = *(DWORD *)pos;  pos += sizeof(DWORD);
348         r->Data.SIG.dwExpiration  = *(DWORD *)pos;  pos += sizeof(DWORD);
349         r->Data.SIG.dwTimeSigned  = *(DWORD *)pos;  pos += sizeof(DWORD);
350         r->Data.SIG.wKeyTag       = *(WORD *)pos;
351
352         size = rr->rdata + rr->rdlength - pos;
353
354         for (i = 0; i < size; i++)
355             r->Data.SIG.Signature[i] = *(BYTE *)pos++;
356
357         *dlen = sizeof(DNS_SIG_DATAA) + (size - 1) * sizeof(BYTE);
358         break; 
359     }
360     case ns_t_soa:
361     {
362         r->Data.SOA.pNamePrimaryServer = dns_dname_from_msg( msg, pos );
363         if (!r->Data.SOA.pNamePrimaryServer) return ERROR_NOT_ENOUGH_MEMORY;
364
365         if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
366             return DNS_ERROR_BAD_PACKET;
367
368         r->Data.SOA.pNameAdministrator = dns_dname_from_msg( msg, pos );
369         if (!r->Data.SOA.pNameAdministrator)
370         {
371             dns_free( r->Data.SOA.pNamePrimaryServer ); 
372             return ERROR_NOT_ENOUGH_MEMORY;
373         }
374
375         if (dns_ns_name_skip( &pos, ns_msg_end( msg ) ) < 0)
376             return DNS_ERROR_BAD_PACKET;
377
378         r->Data.SOA.dwSerialNo   = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
379         r->Data.SOA.dwRefresh    = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
380         r->Data.SOA.dwRetry      = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
381         r->Data.SOA.dwExpire     = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
382         r->Data.SOA.dwDefaultTtl = ntohl( *(DWORD *)pos ); pos += sizeof(DWORD);
383
384         *dlen = sizeof(DNS_SOA_DATAA);
385         break; 
386     }
387     case ns_t_srv:
388     {
389         r->Data.SRV.wPriority = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
390         r->Data.SRV.wWeight   = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
391         r->Data.SRV.wPort     = ntohs( *(WORD *)pos ); pos += sizeof(WORD);
392
393         r->Data.SRV.pNameTarget = dns_dname_from_msg( msg, pos );
394         if (!r->Data.SRV.pNameTarget) return ERROR_NOT_ENOUGH_MEMORY;
395
396         *dlen = sizeof(DNS_SRV_DATAA);
397         break; 
398     }
399     case ns_t_hinfo:
400     case ns_t_isdn:
401     case ns_t_x25:
402     case ns_t_txt:
403     {
404         i = 0;
405         while (pos[0] && pos < rr->rdata + rr->rdlength)
406         {
407             r->Data.TXT.pStringArray[i] = dns_str_from_rdata( pos );
408             if (!r->Data.TXT.pStringArray[i])
409             {
410                 for (--i; i >= 0; i--)
411                     dns_free( r->Data.TXT.pStringArray[i] );
412                 return ERROR_NOT_ENOUGH_MEMORY;
413             }
414             i++;
415             pos += pos[0] + 1;
416         }
417         r->Data.TXT.dwStringCount = i;
418         *dlen = sizeof(DNS_TXT_DATAA) + (i - 1) * sizeof(PCHAR);
419         break;
420     }
421     case ns_t_atma:
422     case ns_t_loc:
423     case ns_t_nxt:
424     case ns_t_tsig:
425     case ns_t_wks:
426     case 0x00f9:  /* TKEY */
427     case 0xff01:  /* WINS */
428     case 0xff02:  /* WINSR */
429     default:
430         FIXME( "unhandled type: %s\n", dns_type_to_str( rr->type ) );
431         return DNS_ERROR_RCODE_NOT_IMPLEMENTED;
432     }
433
434     return ret;
435
436
437 static DNS_STATUS dns_copy_record( ns_msg msg, ns_sect section,
438                                    unsigned short num, DNS_RECORDA **recp )
439 {
440     DNS_STATUS ret;
441     DNS_RECORDA *record;
442     WORD dlen;
443     ns_rr rr;
444
445     if (dns_ns_parserr( &msg, section, num, &rr ) < 0)
446         return DNS_ERROR_BAD_PACKET;
447
448     if (!(record = dns_zero_alloc( dns_get_record_size( &rr ) )))
449         return ERROR_NOT_ENOUGH_MEMORY;
450
451     record->pName = dns_strdup_u( rr.name );
452     if (!record->pName)
453     {
454         dns_free( record );
455         return ERROR_NOT_ENOUGH_MEMORY;
456     }
457
458     record->wType = rr.type;
459     record->Flags.S.Section = section;
460     record->Flags.S.CharSet = DnsCharSetUtf8;
461     record->dwTtl = rr.ttl;
462
463     if ((ret = dns_copy_rdata( msg, &rr, record, &dlen )))
464     {
465         dns_free( record->pName );
466         dns_free( record );
467         return ret;
468     }
469     record->wDataLength = dlen;
470     *recp = record;
471
472     TRACE( "found %s record in %s section\n",
473            dns_type_to_str( rr.type ), dns_section_to_str( section ) );
474     return ERROR_SUCCESS;
475 }
476
477 #define DEFAULT_TTL  1200
478
479 static DNS_STATUS dns_do_query_netbios( PCSTR name, DNS_RECORDA **recp )
480 {
481     NCB ncb;
482     UCHAR ret;
483     DNS_RRSET rrset;
484     FIND_NAME_BUFFER *buffer;
485     FIND_NAME_HEADER *header;
486     DNS_RECORDA *record = NULL;
487     unsigned int i, len;
488
489     len = strlen( name );
490     if (len >= NCBNAMSZ) return DNS_ERROR_RCODE_NAME_ERROR;
491
492     DNS_RRSET_INIT( rrset );
493
494     memset( &ncb, 0, sizeof(ncb) );
495     ncb.ncb_command = NCBFINDNAME;
496
497     memset( ncb.ncb_callname, ' ', sizeof(ncb.ncb_callname) );
498     memcpy( ncb.ncb_callname, name, len );
499     ncb.ncb_callname[NCBNAMSZ] = '\0';
500
501     ret = Netbios( &ncb );
502     if (ret != NRC_GOODRET) return ERROR_INVALID_NAME;
503
504     header = (FIND_NAME_HEADER *)ncb.ncb_buffer;
505     buffer = (FIND_NAME_BUFFER *)((char *)header + sizeof(FIND_NAME_HEADER));
506
507     for (i = 0; i < header->node_count; i++) 
508     {
509         record = dns_zero_alloc( sizeof(DNS_RECORDA) );
510         if (!record)
511         {
512             ret = ERROR_NOT_ENOUGH_MEMORY;
513             goto exit;
514         }
515         else
516         {
517             record->pName = dns_strdup_u( name );
518             if (!record->pName)
519             {
520                 ret = ERROR_NOT_ENOUGH_MEMORY;
521                 goto exit;
522             }
523
524             record->wType = DNS_TYPE_A;
525             record->Flags.S.Section = DnsSectionAnswer;
526             record->Flags.S.CharSet = DnsCharSetUtf8;
527             record->dwTtl = DEFAULT_TTL;
528
529             /* FIXME: network byte order? */
530             record->Data.A.IpAddress = *(DWORD *)((char *)buffer[i].destination_addr + 2);
531
532             DNS_RRSET_ADD( rrset, (DNS_RECORD *)record );
533         }
534     }
535
536 exit:
537     DNS_RRSET_TERMINATE( rrset );
538
539     if (ret != ERROR_SUCCESS)
540         DnsRecordListFree( (DNS_RECORD *)record, DnsFreeRecordList );
541     else
542         *recp = (DNS_RECORDA *)rrset.pFirstRR;
543
544     return ret;
545 }
546
547 /*  The resolver lock must be held and res_init() must have been
548  *  called before calling these three functions.
549  */
550 static DNS_STATUS dns_set_serverlist( PIP4_ARRAY addrs )
551 {
552     unsigned int i;
553
554     if (addrs->AddrCount > MAXNS) 
555     {
556         WARN( "too many servers: %ld only using the first: %d\n",
557               addrs->AddrCount, MAXNS );
558         _res.nscount = MAXNS;
559     }
560     else _res.nscount = addrs->AddrCount;
561
562     for (i = 0; i < _res.nscount; i++)
563         _res.nsaddr_list[i].sin_addr.s_addr = addrs->AddrArray[i];
564
565     return ERROR_SUCCESS;
566 }
567
568 static DNS_STATUS dns_get_serverlist( PIP4_ARRAY addrs, PDWORD len )
569 {
570     unsigned int i, size;
571
572     size = sizeof(IP4_ARRAY) + sizeof(IP4_ADDRESS) * (_res.nscount - 1);
573     if (!addrs || *len < size)
574     {
575         *len = size;
576         return ERROR_INSUFFICIENT_BUFFER;
577     }
578
579     addrs->AddrCount = _res.nscount;
580
581     for (i = 0; i < _res.nscount; i++)
582         addrs->AddrArray[i] = _res.nsaddr_list[i].sin_addr.s_addr;
583
584     return ERROR_SUCCESS;
585 }
586
587 static DNS_STATUS dns_do_query( PCSTR name, WORD type, DWORD options,
588                                 PDNS_RECORDA *result )
589 {
590     DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;
591
592     unsigned int i, num;
593     unsigned char answer[NS_PACKETSZ];
594     ns_sect sections[] = { ns_s_an, ns_s_ar };
595     ns_msg msg;
596
597     DNS_RECORDA *record = NULL;
598     DNS_RRSET rrset;
599     int len;
600
601     DNS_RRSET_INIT( rrset );
602
603     len = res_query( name, ns_c_in, type, answer, sizeof(answer) );
604     if (len < 0)
605     {
606         ret = dns_map_h_errno( h_errno );
607         goto exit;
608     }
609
610     if (dns_ns_initparse( answer, len, &msg ) < 0)
611     {
612         ret = DNS_ERROR_BAD_PACKET;
613         goto exit;
614     }
615
616 #define RCODE_MASK 0x0f
617     if ((msg._flags & RCODE_MASK) != ns_r_noerror)
618     {
619         ret = dns_map_error( msg._flags & RCODE_MASK );
620         goto exit;
621     }
622
623     for (i = 0; i < sizeof(sections)/sizeof(sections[0]); i++)
624     {
625         for (num = 0; num < ns_msg_count( msg, sections[i] ); num++)
626         {
627             ret = dns_copy_record( msg, sections[i], num, &record );
628             if (ret != ERROR_SUCCESS) goto exit;
629
630             DNS_RRSET_ADD( rrset, (DNS_RECORD *)record );
631         }
632     }
633
634 exit:
635     DNS_RRSET_TERMINATE( rrset );
636
637     if (ret != ERROR_SUCCESS)
638         DnsRecordListFree( rrset.pFirstRR, DnsFreeRecordList );
639     else
640         *result = (DNS_RECORDA *)rrset.pFirstRR;
641
642     return ret;
643 }
644
645 #endif /* HAVE_RESOLV */
646
647 /******************************************************************************
648  * DnsQuery_A           [DNSAPI.@]
649  *
650  */
651 DNS_STATUS WINAPI DnsQuery_A( PCSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
652                               PDNS_RECORDA *result, PVOID *reserved )
653 {
654     WCHAR *nameW;
655     DNS_RECORDW *resultW;
656     DNS_STATUS status;
657
658     TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
659            options, servers, result, reserved );
660
661     if (!name || !result)
662         return ERROR_INVALID_PARAMETER;
663
664     nameW = dns_strdup_aw( name );
665     if (!nameW) return ERROR_NOT_ENOUGH_MEMORY;
666
667     status = DnsQuery_W( nameW, type, options, servers, &resultW, reserved ); 
668
669     if (status == ERROR_SUCCESS)
670     {
671         *result = (DNS_RECORDA *)DnsRecordSetCopyEx(
672              (DNS_RECORD *)resultW, DnsCharSetUnicode, DnsCharSetAnsi );
673
674         if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
675         DnsRecordListFree( (DNS_RECORD *)resultW, DnsFreeRecordList );
676     }
677
678     dns_free( nameW );
679     return status;
680 }
681
682 /******************************************************************************
683  * DnsQuery_UTF8              [DNSAPI.@]
684  *
685  */
686 DNS_STATUS WINAPI DnsQuery_UTF8( PCSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
687                                  PDNS_RECORDA *result, PVOID *reserved )
688 {
689     DNS_STATUS ret = DNS_ERROR_RCODE_NOT_IMPLEMENTED;
690 #ifdef HAVE_RESOLV
691
692     TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_a(name), dns_type_to_str( type ),
693            options, servers, result, reserved );
694
695     if (!name || !result)
696         return ERROR_INVALID_PARAMETER;
697
698     LOCK_RESOLVER();
699
700     res_init();
701     _res.options |= dns_map_options( options );
702
703     if (servers && (ret = dns_set_serverlist( servers )))
704     {
705         UNLOCK_RESOLVER();
706         return ret;
707     }
708
709     ret = dns_do_query( name, type, options, result );
710
711     if (ret == DNS_ERROR_RCODE_NAME_ERROR && type == DNS_TYPE_A &&
712         !(options & DNS_QUERY_NO_NETBT))
713     {
714         TRACE( "dns lookup failed, trying netbios query\n" );
715         ret = dns_do_query_netbios( name, result );
716     }
717
718     UNLOCK_RESOLVER();
719
720 #endif
721     return ret;
722 }
723
724 /******************************************************************************
725  * DnsQuery_W              [DNSAPI.@]
726  *
727  */
728 DNS_STATUS WINAPI DnsQuery_W( PCWSTR name, WORD type, DWORD options, PIP4_ARRAY servers,
729                               PDNS_RECORDW *result, PVOID *reserved )
730 {
731     char *nameU;
732     DNS_RECORDA *resultA;
733     DNS_STATUS status;
734
735     TRACE( "(%s,%s,0x%08lx,%p,%p,%p)\n", debugstr_w(name), dns_type_to_str( type ),
736            options, servers, result, reserved );
737
738     if (!name || !result)
739         return ERROR_INVALID_PARAMETER;
740
741     nameU = dns_strdup_wu( name );
742     if (!nameU) return ERROR_NOT_ENOUGH_MEMORY;
743
744     status = DnsQuery_UTF8( nameU, type, options, servers, &resultA, reserved ); 
745
746     if (status == ERROR_SUCCESS)
747     {
748         *result = (DNS_RECORDW *)DnsRecordSetCopyEx(
749             (DNS_RECORD *)resultA, DnsCharSetUtf8, DnsCharSetUnicode );
750
751         if (!*result) status = ERROR_NOT_ENOUGH_MEMORY;
752         DnsRecordListFree( (DNS_RECORD *)resultA, DnsFreeRecordList );
753     }
754
755     dns_free( nameU );
756     return status;
757 }
758
759 static DNS_STATUS dns_get_hostname_a( COMPUTER_NAME_FORMAT format,
760                                       LPSTR buffer, PDWORD len )
761 {
762     char name[256];
763     DWORD size = sizeof(name);
764
765     if (!GetComputerNameExA( format, name, &size ))
766         return DNS_ERROR_NAME_DOES_NOT_EXIST;
767
768     if (!buffer || (size = lstrlenA( name ) + 1) > *len)
769     {
770         *len = size;
771         return ERROR_INSUFFICIENT_BUFFER;
772     }
773
774     lstrcpyA( buffer, name );
775     return ERROR_SUCCESS;
776 }
777
778 static DNS_STATUS dns_get_hostname_w( COMPUTER_NAME_FORMAT format,
779                                       LPWSTR buffer, PDWORD len )
780 {
781     WCHAR name[256];
782     DWORD size = sizeof(name);
783
784     if (!GetComputerNameExW( format, name, &size ))
785         return DNS_ERROR_NAME_DOES_NOT_EXIST;
786
787     if (!buffer || (size = lstrlenW( name ) + 1) > *len)
788     {
789         *len = size;
790         return ERROR_INSUFFICIENT_BUFFER;
791     }
792
793     lstrcpyW( buffer, name );
794     return ERROR_SUCCESS;
795 }
796
797 /******************************************************************************
798  * DnsQueryConfig          [DNSAPI.@]
799  *
800  */
801 DNS_STATUS WINAPI DnsQueryConfig( DNS_CONFIG_TYPE config, DWORD flag, PWSTR adapter,
802                                   PVOID reserved, PVOID buffer, PDWORD len )
803 {
804     DNS_STATUS ret = ERROR_INVALID_PARAMETER;
805
806     TRACE( "(%d,0x%08lx,%s,%p,%p,%p)\n", config, flag, debugstr_w(adapter),
807            reserved, buffer, len );
808
809     if (!len) return ERROR_INVALID_PARAMETER;
810
811     switch (config)
812     {
813     case DnsConfigDnsServerList:
814     {
815 #ifdef HAVE_RESOLV
816         LOCK_RESOLVER();
817
818         res_init();
819         ret = dns_get_serverlist( (IP4_ARRAY *)buffer, len );
820
821         UNLOCK_RESOLVER();
822         break;
823 #else
824         WARN( "compiled without resolver support\n" );
825         break;
826 #endif
827     }
828     case DnsConfigHostName_A:
829     case DnsConfigHostName_UTF8:
830         return dns_get_hostname_a( ComputerNameDnsHostname, buffer, len );
831
832     case DnsConfigFullHostName_A:
833     case DnsConfigFullHostName_UTF8:
834         return dns_get_hostname_a( ComputerNameDnsFullyQualified, buffer, len );
835
836     case DnsConfigPrimaryDomainName_A:
837     case DnsConfigPrimaryDomainName_UTF8:
838         return dns_get_hostname_a( ComputerNameDnsDomain, buffer, len );
839
840     case DnsConfigHostName_W:
841         return dns_get_hostname_w( ComputerNameDnsHostname, buffer, len );
842
843     case DnsConfigFullHostName_W:
844         return dns_get_hostname_w( ComputerNameDnsFullyQualified, buffer, len );
845
846     case DnsConfigPrimaryDomainName_W:
847         return dns_get_hostname_w( ComputerNameDnsDomain, buffer, len );
848
849     case DnsConfigAdapterDomainName_A:
850     case DnsConfigAdapterDomainName_W:
851     case DnsConfigAdapterDomainName_UTF8:
852     case DnsConfigSearchList:
853     case DnsConfigAdapterInfo:
854     case DnsConfigPrimaryHostNameRegistrationEnabled:
855     case DnsConfigAdapterHostNameRegistrationEnabled:
856     case DnsConfigAddressRegistrationMaxCount:
857         FIXME( "unimplemented config type %d\n", config );
858         break;
859
860     default:
861         WARN( "unknown config type: %d\n", config );
862         break;
863     }
864     return ret;
865 }