2 * Unix interface for libresolv
4 * Copyright 2021 Hans Leidekker for CodeWeavers
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.
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.
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
31 #include <sys/types.h>
33 #ifdef HAVE_NETINET_IN_H
34 # include <netinet/in.h>
36 #ifdef HAVE_ARPA_NAMESER_H
37 # include <arpa/nameser.h>
47 #define WIN32_NO_STATUS
54 #include "wine/debug.h"
55 #include "wine/heap.h"
58 WINE_DEFAULT_DEBUG_CHANNEL(dnsapi
);
60 static const char *debugstr_type( unsigned short type
)
66 #define X(x) case (x): str = #x; break;
125 return wine_dbg_sprintf( "0x%04x", type
);
128 return wine_dbg_sprintf( "%s", str
);
131 static const char *debugstr_section( ns_sect section
)
135 case ns_s_qd
: return "Question";
136 case ns_s_an
: return "Answer";
137 case ns_s_ns
: return "Authority";
138 case ns_s_ar
: return "Additional";
140 return wine_dbg_sprintf( "0x%02x", section
);
144 /* call res_init() just once because of a bug in Mac OS X 10.4 */
145 /* call once per thread on systems that have per-thread _res */
146 static void init_resolver( void )
148 if (!(_res
.options
& RES_INIT
)) res_init();
151 static unsigned long map_options( DWORD options
)
153 unsigned long ret
= 0;
155 if (options
== DNS_QUERY_STANDARD
)
158 if (options
& DNS_QUERY_ACCEPT_TRUNCATED_RESPONSE
)
160 if (options
& DNS_QUERY_USE_TCP_ONLY
)
162 if (options
& DNS_QUERY_NO_RECURSION
)
164 if (options
& DNS_QUERY_NO_LOCAL_NAME
)
166 if (options
& DNS_QUERY_NO_HOSTS_FILE
)
167 ret
|= RES_NOALIASES
;
168 if (options
& DNS_QUERY_TREAT_AS_FQDN
)
169 ret
&= ~RES_DEFNAMES
;
171 if (options
& DNS_QUERY_DONT_RESET_TTL_VALUES
)
172 FIXME( "option DNS_QUERY_DONT_RESET_TTL_VALUES not implemented\n" );
173 if (options
& DNS_QUERY_RESERVED
)
174 FIXME( "option DNS_QUERY_RESERVED not implemented\n" );
175 if (options
& DNS_QUERY_WIRE_ONLY
)
176 FIXME( "option DNS_QUERY_WIRE_ONLY not implemented\n" );
177 if (options
& DNS_QUERY_NO_WIRE_QUERY
)
178 FIXME( "option DNS_QUERY_NO_WIRE_QUERY not implemented\n" );
179 if (options
& DNS_QUERY_BYPASS_CACHE
)
180 FIXME( "option DNS_QUERY_BYPASS_CACHE not implemented\n" );
181 if (options
& DNS_QUERY_RETURN_MESSAGE
)
182 FIXME( "option DNS_QUERY_RETURN_MESSAGE not implemented\n" );
184 if (options
& DNS_QUERY_NO_NETBT
)
185 TRACE( "netbios query disabled\n" );
190 static DNS_STATUS
map_error( int error
)
194 case ns_r_noerror
: return ERROR_SUCCESS
;
195 case ns_r_formerr
: return DNS_ERROR_RCODE_FORMAT_ERROR
;
196 case ns_r_servfail
: return DNS_ERROR_RCODE_SERVER_FAILURE
;
197 case ns_r_nxdomain
: return DNS_ERROR_RCODE_NAME_ERROR
;
198 case ns_r_notimpl
: return DNS_ERROR_RCODE_NOT_IMPLEMENTED
;
199 case ns_r_refused
: return DNS_ERROR_RCODE_REFUSED
;
200 case ns_r_yxdomain
: return DNS_ERROR_RCODE_YXDOMAIN
;
201 case ns_r_yxrrset
: return DNS_ERROR_RCODE_YXRRSET
;
202 case ns_r_nxrrset
: return DNS_ERROR_RCODE_NXRRSET
;
203 case ns_r_notauth
: return DNS_ERROR_RCODE_NOTAUTH
;
204 case ns_r_notzone
: return DNS_ERROR_RCODE_NOTZONE
;
206 FIXME( "unmapped error code: %d\n", error
);
207 return DNS_ERROR_RCODE_NOT_IMPLEMENTED
;
211 static DNS_STATUS
map_h_errno( int error
)
216 case HOST_NOT_FOUND
: return DNS_ERROR_RCODE_NAME_ERROR
;
217 case TRY_AGAIN
: return DNS_ERROR_RCODE_SERVER_FAILURE
;
218 case NO_RECOVERY
: return DNS_ERROR_RCODE_REFUSED
;
219 #ifdef NETDB_INTERNAL
220 case NETDB_INTERNAL
: return DNS_ERROR_RCODE
;
223 FIXME( "unmapped error code: %d\n", error
);
224 return DNS_ERROR_RCODE_NOT_IMPLEMENTED
;
228 DNS_STATUS CDECL
resolv_get_serverlist( IP4_ARRAY
*addrs
, DWORD
*len
)
235 size
= FIELD_OFFSET(IP4_ARRAY
, AddrArray
[_res
.nscount
]);
236 if (!addrs
|| *len
< size
)
239 return ERROR_INSUFFICIENT_BUFFER
;
242 addrs
->AddrCount
= _res
.nscount
;
244 for (i
= 0; i
< _res
.nscount
; i
++)
245 addrs
->AddrArray
[i
] = _res
.nsaddr_list
[i
].sin_addr
.s_addr
;
247 return ERROR_SUCCESS
;
250 DNS_STATUS CDECL
resolv_set_serverlist( const IP4_ARRAY
*addrs
)
256 if (!addrs
|| !addrs
->AddrCount
) return ERROR_SUCCESS
;
257 if (addrs
->AddrCount
> MAXNS
)
259 WARN( "too many servers: %d only using the first: %d\n",
260 addrs
->AddrCount
, MAXNS
);
261 _res
.nscount
= MAXNS
;
263 else _res
.nscount
= addrs
->AddrCount
;
265 for (i
= 0; i
< _res
.nscount
; i
++)
266 _res
.nsaddr_list
[i
].sin_addr
.s_addr
= addrs
->AddrArray
[i
];
268 return ERROR_SUCCESS
;
271 static char *dname_from_msg( ns_msg msg
, const unsigned char *pos
)
273 char *str
, dname
[NS_MAXDNAME
] = ".";
275 /* returns *compressed* length, ignore it */
276 ns_name_uncompress( ns_msg_base(msg
), ns_msg_end(msg
), pos
, dname
, sizeof(dname
) );
278 if ((str
= RtlAllocateHeap( GetProcessHeap(), 0, strlen(dname
) + 1 ))) strcpy( str
, dname
);
282 static char *str_from_rdata( const unsigned char *rdata
)
285 unsigned int len
= rdata
[0];
287 if ((str
= RtlAllocateHeap( GetProcessHeap(), 0, len
+ 1 )))
289 memcpy( str
, ++rdata
, len
);
295 static unsigned int get_record_size( const ns_rr
*rr
)
297 const unsigned char *pos
= rr
->rdata
;
298 unsigned int num
= 0, size
= sizeof(DNS_RECORDA
);
304 pos
+= sizeof(WORD
) + sizeof(BYTE
) + sizeof(BYTE
);
305 size
+= rr
->rdata
+ rr
->rdlength
- pos
- 1;
310 pos
+= sizeof(PCHAR
) + sizeof(WORD
) + 2 * sizeof(BYTE
);
311 pos
+= 3 * sizeof(DWORD
) + 2 * sizeof(WORD
);
312 size
+= rr
->rdata
+ rr
->rdlength
- pos
- 1;
320 while (pos
[0] && pos
< rr
->rdata
+ rr
->rdlength
)
325 size
+= (num
- 1) * sizeof(PCHAR
);
331 size
+= rr
->rdlength
- 1;
336 case 0xff01: /* WINS */
338 FIXME( "unhandled type: %s\n", debugstr_type( rr
->type
) );
347 static DNS_STATUS
copy_rdata( ns_msg msg
, const ns_rr
*rr
, DNS_RECORDA
*r
, WORD
*dlen
)
349 DNS_STATUS ret
= ERROR_SUCCESS
;
350 const unsigned char *pos
= rr
->rdata
;
351 unsigned int i
, size
;
357 r
->Data
.A
.IpAddress
= *(const DWORD
*)pos
;
358 *dlen
= sizeof(DNS_A_DATA
);
363 for (i
= 0; i
< sizeof(IP6_ADDRESS
)/sizeof(DWORD
); i
++)
365 r
->Data
.AAAA
.Ip6Address
.IP6Dword
[i
] = *(const DWORD
*)pos
;
366 pos
+= sizeof(DWORD
);
369 *dlen
= sizeof(DNS_AAAA_DATA
);
374 /* FIXME: byte order? */
375 r
->Data
.KEY
.wFlags
= *(const WORD
*)pos
; pos
+= sizeof(WORD
);
376 r
->Data
.KEY
.chProtocol
= *pos
++;
377 r
->Data
.KEY
.chAlgorithm
= *pos
++;
379 size
= rr
->rdata
+ rr
->rdlength
- pos
;
381 for (i
= 0; i
< size
; i
++)
382 r
->Data
.KEY
.Key
[i
] = *pos
++;
384 *dlen
= sizeof(DNS_KEY_DATA
) + (size
- 1) * sizeof(BYTE
);
390 r
->Data
.MINFO
.pNameMailbox
= dname_from_msg( msg
, pos
);
391 if (!r
->Data
.MINFO
.pNameMailbox
) return ERROR_NOT_ENOUGH_MEMORY
;
393 if (ns_name_skip( &pos
, ns_msg_end( msg
) ) < 0)
394 return DNS_ERROR_BAD_PACKET
;
396 r
->Data
.MINFO
.pNameErrorsMailbox
= dname_from_msg( msg
, pos
);
397 if (!r
->Data
.MINFO
.pNameErrorsMailbox
)
399 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.MINFO
.pNameMailbox
);
400 return ERROR_NOT_ENOUGH_MEMORY
;
403 *dlen
= sizeof(DNS_MINFO_DATAA
);
410 r
->Data
.MX
.wPreference
= ntohs( *(const WORD
*)pos
);
411 r
->Data
.MX
.pNameExchange
= dname_from_msg( msg
, pos
+ sizeof(WORD
) );
412 if (!r
->Data
.MX
.pNameExchange
) return ERROR_NOT_ENOUGH_MEMORY
;
414 *dlen
= sizeof(DNS_MX_DATAA
);
419 r
->Data
.Null
.dwByteCount
= rr
->rdlength
;
420 memcpy( r
->Data
.Null
.Data
, rr
->rdata
, rr
->rdlength
);
422 *dlen
= sizeof(DNS_NULL_DATA
) + rr
->rdlength
- 1;
427 r
->Data
.OPT
.wDataLength
= rr
->rdlength
;
428 r
->Data
.OPT
.wPad
= 0;
429 memcpy( r
->Data
.OPT
.Data
, rr
->rdata
, rr
->rdlength
);
431 *dlen
= sizeof(DNS_OPT_DATA
) + rr
->rdlength
- 1;
443 r
->Data
.PTR
.pNameHost
= dname_from_msg( msg
, pos
);
444 if (!r
->Data
.PTR
.pNameHost
) return ERROR_NOT_ENOUGH_MEMORY
;
446 *dlen
= sizeof(DNS_PTR_DATAA
);
451 r
->Data
.SIG
.pNameSigner
= dname_from_msg( msg
, pos
);
452 if (!r
->Data
.SIG
.pNameSigner
) return ERROR_NOT_ENOUGH_MEMORY
;
454 if (ns_name_skip( &pos
, ns_msg_end( msg
) ) < 0)
455 return DNS_ERROR_BAD_PACKET
;
457 /* FIXME: byte order? */
458 r
->Data
.SIG
.wTypeCovered
= *(const WORD
*)pos
; pos
+= sizeof(WORD
);
459 r
->Data
.SIG
.chAlgorithm
= *pos
++;
460 r
->Data
.SIG
.chLabelCount
= *pos
++;
461 r
->Data
.SIG
.dwOriginalTtl
= *(const DWORD
*)pos
; pos
+= sizeof(DWORD
);
462 r
->Data
.SIG
.dwExpiration
= *(const DWORD
*)pos
; pos
+= sizeof(DWORD
);
463 r
->Data
.SIG
.dwTimeSigned
= *(const DWORD
*)pos
; pos
+= sizeof(DWORD
);
464 r
->Data
.SIG
.wKeyTag
= *(const WORD
*)pos
;
466 size
= rr
->rdata
+ rr
->rdlength
- pos
;
468 for (i
= 0; i
< size
; i
++)
469 r
->Data
.SIG
.Signature
[i
] = *pos
++;
471 *dlen
= sizeof(DNS_SIG_DATAA
) + (size
- 1) * sizeof(BYTE
);
476 r
->Data
.SOA
.pNamePrimaryServer
= dname_from_msg( msg
, pos
);
477 if (!r
->Data
.SOA
.pNamePrimaryServer
) return ERROR_NOT_ENOUGH_MEMORY
;
479 if (ns_name_skip( &pos
, ns_msg_end( msg
) ) < 0)
480 return DNS_ERROR_BAD_PACKET
;
482 r
->Data
.SOA
.pNameAdministrator
= dname_from_msg( msg
, pos
);
483 if (!r
->Data
.SOA
.pNameAdministrator
)
485 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.SOA
.pNamePrimaryServer
);
486 return ERROR_NOT_ENOUGH_MEMORY
;
489 if (ns_name_skip( &pos
, ns_msg_end( msg
) ) < 0)
490 return DNS_ERROR_BAD_PACKET
;
492 r
->Data
.SOA
.dwSerialNo
= ntohl( *(const DWORD
*)pos
); pos
+= sizeof(DWORD
);
493 r
->Data
.SOA
.dwRefresh
= ntohl( *(const DWORD
*)pos
); pos
+= sizeof(DWORD
);
494 r
->Data
.SOA
.dwRetry
= ntohl( *(const DWORD
*)pos
); pos
+= sizeof(DWORD
);
495 r
->Data
.SOA
.dwExpire
= ntohl( *(const DWORD
*)pos
); pos
+= sizeof(DWORD
);
496 r
->Data
.SOA
.dwDefaultTtl
= ntohl( *(const DWORD
*)pos
); pos
+= sizeof(DWORD
);
498 *dlen
= sizeof(DNS_SOA_DATAA
);
503 r
->Data
.SRV
.wPriority
= ntohs( *(const WORD
*)pos
); pos
+= sizeof(WORD
);
504 r
->Data
.SRV
.wWeight
= ntohs( *(const WORD
*)pos
); pos
+= sizeof(WORD
);
505 r
->Data
.SRV
.wPort
= ntohs( *(const WORD
*)pos
); pos
+= sizeof(WORD
);
507 r
->Data
.SRV
.pNameTarget
= dname_from_msg( msg
, pos
);
508 if (!r
->Data
.SRV
.pNameTarget
) return ERROR_NOT_ENOUGH_MEMORY
;
510 *dlen
= sizeof(DNS_SRV_DATAA
);
519 while (pos
[0] && pos
< rr
->rdata
+ rr
->rdlength
)
521 r
->Data
.TXT
.pStringArray
[i
] = str_from_rdata( pos
);
522 if (!r
->Data
.TXT
.pStringArray
[i
])
524 while (i
> 0) RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.TXT
.pStringArray
[--i
] );
525 return ERROR_NOT_ENOUGH_MEMORY
;
530 r
->Data
.TXT
.dwStringCount
= i
;
531 *dlen
= sizeof(DNS_TXT_DATAA
) + (i
- 1) * sizeof(PCHAR
);
539 case 0x00f9: /* TKEY */
540 case 0xff01: /* WINS */
541 case 0xff02: /* WINSR */
543 FIXME( "unhandled type: %s\n", debugstr_type( rr
->type
) );
544 return DNS_ERROR_RCODE_NOT_IMPLEMENTED
;
550 static inline char *heap_strdup( const char *src
)
553 if (!src
) return NULL
;
554 if ((dst
= RtlAllocateHeap( GetProcessHeap(), 0, (strlen( src
) + 1) * sizeof(char) ))) strcpy( dst
, src
);
558 static DNS_STATUS
copy_record( ns_msg msg
, ns_sect section
, unsigned short num
, DNS_RECORDA
**recp
)
565 if (ns_parserr( &msg
, section
, num
, &rr
) < 0)
566 return DNS_ERROR_BAD_PACKET
;
568 if (!(record
= RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY
, get_record_size( &rr
) )))
569 return ERROR_NOT_ENOUGH_MEMORY
;
571 if (!(record
->pName
= heap_strdup( rr
.name
)))
573 RtlFreeHeap( GetProcessHeap(), 0, record
);
574 return ERROR_NOT_ENOUGH_MEMORY
;
577 record
->wType
= rr
.type
;
578 record
->Flags
.S
.Section
= section
;
579 record
->Flags
.S
.CharSet
= DnsCharSetUtf8
;
580 record
->dwTtl
= rr
.ttl
;
582 if ((ret
= copy_rdata( msg
, &rr
, record
, &dlen
)))
584 RtlFreeHeap( GetProcessHeap(), 0, record
->pName
);
585 RtlFreeHeap( GetProcessHeap(), 0, record
);
588 record
->wDataLength
= dlen
;
591 TRACE( "found %s record in %s section\n", debugstr_type( rr
.type
), debugstr_section( section
) );
592 return ERROR_SUCCESS
;
595 static void free_record_list( DNS_RECORD
*list
)
597 DNS_RECORD
*r
, *next
;
600 for (r
= list
; (list
= r
); r
= next
)
602 RtlFreeHeap( GetProcessHeap(), 0, r
->pName
);
611 for (i
= 0; i
< r
->Data
.TXT
.dwStringCount
; i
++)
612 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.TXT
.pStringArray
[i
] );
618 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.MINFO
.pNameMailbox
);
619 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.MINFO
.pNameErrorsMailbox
);
626 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.MX
.pNameExchange
);
631 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.NXT
.pNameNext
);
643 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.PTR
.pNameHost
);
648 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.SIG
.pNameSigner
);
653 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.SOA
.pNamePrimaryServer
);
654 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.SOA
.pNameAdministrator
);
659 RtlFreeHeap( GetProcessHeap(), 0, r
->Data
.SRV
.pNameTarget
);
666 RtlFreeHeap( GetProcessHeap(), 0, r
);
670 #define DNS_MAX_PACKET_SIZE 4096
671 DNS_STATUS CDECL
resolv_query( const char *name
, WORD type
, DWORD options
, DNS_RECORDA
**result
)
673 DNS_STATUS ret
= DNS_ERROR_RCODE_NOT_IMPLEMENTED
;
675 unsigned char answer
[DNS_MAX_PACKET_SIZE
];
676 ns_sect sections
[] = { ns_s_an
, ns_s_ar
};
678 DNS_RECORDA
*record
= NULL
;
682 DNS_RRSET_INIT( rrset
);
685 _res
.options
|= map_options( options
);
687 if ((len
= res_query( name
, ns_c_in
, type
, answer
, sizeof(answer
) )) < 0)
689 ret
= map_h_errno( h_errno
);
693 if (ns_initparse( answer
, len
, &msg
) < 0)
695 ret
= DNS_ERROR_BAD_PACKET
;
699 #define RCODE_MASK 0x0f
700 if ((msg
._flags
& RCODE_MASK
) != ns_r_noerror
)
702 ret
= map_error( msg
._flags
& RCODE_MASK
);
706 for (i
= 0; i
< ARRAY_SIZE(sections
); i
++)
708 for (num
= 0; num
< ns_msg_count( msg
, sections
[i
] ); num
++)
710 ret
= copy_record( msg
, sections
[i
], num
, &record
);
711 if (ret
!= ERROR_SUCCESS
) goto exit
;
713 DNS_RRSET_ADD( rrset
, (DNS_RECORD
*)record
);
718 DNS_RRSET_TERMINATE( rrset
);
720 if (ret
!= ERROR_SUCCESS
)
721 free_record_list( rrset
.pFirstRR
);
723 *result
= (DNS_RECORDA
*)rrset
.pFirstRR
;
728 static const struct resolv_funcs funcs
=
730 resolv_get_serverlist
,
732 resolv_set_serverlist
735 NTSTATUS CDECL
__wine_init_unix_lib( HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
737 if (reason
!= DLL_PROCESS_ATTACH
) return STATUS_SUCCESS
;
738 *(const struct resolv_funcs
**)ptr_out
= &funcs
;
739 return STATUS_SUCCESS
;
742 #endif /* HAVE_RESOLV */