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