2012-03-11 13:13:51 +04:00
/*
Unix SMB / CIFS implementation .
Small async DNS library for Samba with socketwrapper support
Copyright ( C ) 2010 Kai Blin < kai @ samba . org >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program 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 General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
# include "system/network.h"
# include <tevent.h>
# include "lib/tsocket/tsocket.h"
# include "libcli/dns/libdns.h"
2015-12-06 13:31:23 +03:00
# include "lib/util/tevent_unix.h"
2012-03-11 13:13:51 +04:00
# include "lib/util/samba_util.h"
2017-12-29 13:01:29 +03:00
# include "lib/util/debug.h"
2012-03-11 13:13:51 +04:00
# include "libcli/util/error.h"
2017-12-29 13:01:29 +03:00
# include "librpc/ndr/libndr.h"
# include "librpc/gen_ndr/ndr_dns.h"
2012-03-11 13:13:51 +04:00
struct dns_udp_request_state {
struct tevent_context * ev ;
struct tdgram_context * dgram ;
size_t query_len ;
uint8_t * reply ;
size_t reply_len ;
} ;
2019-01-22 20:24:35 +03:00
# define DNS_REQUEST_TIMEOUT 10
2012-10-13 04:09:57 +04:00
2012-03-11 13:13:51 +04:00
/* Declare callback functions used below. */
2012-05-30 01:05:14 +04:00
static void dns_udp_request_get_reply ( struct tevent_req * subreq ) ;
static void dns_udp_request_done ( struct tevent_req * subreq ) ;
2012-03-11 13:13:51 +04:00
2018-01-04 23:26:58 +03:00
static struct tevent_req * dns_udp_request_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const char * server_addr_string ,
const uint8_t * query ,
size_t query_len )
2012-03-11 13:13:51 +04:00
{
struct tevent_req * req , * subreq ;
struct dns_udp_request_state * state ;
struct tsocket_address * local_addr , * server_addr ;
struct tdgram_context * dgram ;
int ret ;
req = tevent_req_create ( mem_ctx , & state , struct dns_udp_request_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
/* Use connected UDP sockets */
ret = tsocket_address_inet_from_strings ( state , " ip " , NULL , 0 ,
& local_addr ) ;
if ( ret ! = 0 ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , errno ) ;
2012-03-11 13:13:51 +04:00
return tevent_req_post ( req , ev ) ;
}
2021-09-18 01:02:11 +03:00
ret = tsocket_address_inet_from_hostport_strings (
state , " ip " , server_addr_string , DNS_SERVICE_PORT , & server_addr ) ;
2012-03-11 13:13:51 +04:00
if ( ret ! = 0 ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , errno ) ;
2012-03-11 13:13:51 +04:00
return tevent_req_post ( req , ev ) ;
}
ret = tdgram_inet_udp_socket ( local_addr , server_addr , state , & dgram ) ;
if ( ret ! = 0 ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , errno ) ;
2012-03-11 13:13:51 +04:00
return tevent_req_post ( req , ev ) ;
}
state - > dgram = dgram ;
state - > query_len = query_len ;
dump_data ( 10 , query , query_len ) ;
subreq = tdgram_sendto_send ( state , ev , dgram , query , query_len , NULL ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
2012-10-13 04:09:57 +04:00
if ( ! tevent_req_set_endtime ( req , ev ,
timeval_current_ofs ( DNS_REQUEST_TIMEOUT , 0 ) ) ) {
return tevent_req_post ( req , ev ) ;
}
2012-05-30 01:05:14 +04:00
tevent_req_set_callback ( subreq , dns_udp_request_get_reply , req ) ;
2012-03-11 13:13:51 +04:00
return req ;
}
2012-05-30 01:05:14 +04:00
static void dns_udp_request_get_reply ( struct tevent_req * subreq )
2012-03-11 13:13:51 +04:00
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct dns_udp_request_state * state = tevent_req_data ( req ,
struct dns_udp_request_state ) ;
ssize_t len ;
int err = 0 ;
len = tdgram_sendto_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( len = = - 1 & & err ! = 0 ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , err ) ;
2012-03-11 13:13:51 +04:00
return ;
}
if ( len ! = state - > query_len ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , EIO ) ;
2012-03-11 13:13:51 +04:00
return ;
}
subreq = tdgram_recvfrom_send ( state , state - > ev , state - > dgram ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
2012-05-30 01:05:14 +04:00
tevent_req_set_callback ( subreq , dns_udp_request_done , req ) ;
2012-03-11 13:13:51 +04:00
}
2012-05-30 01:05:14 +04:00
static void dns_udp_request_done ( struct tevent_req * subreq )
2012-03-11 13:13:51 +04:00
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct dns_udp_request_state * state = tevent_req_data ( req ,
struct dns_udp_request_state ) ;
ssize_t len ;
int err = 0 ;
len = tdgram_recvfrom_recv ( subreq , & err , state , & state - > reply , NULL ) ;
TALLOC_FREE ( subreq ) ;
if ( len = = - 1 & & err ! = 0 ) {
2015-12-06 13:31:23 +03:00
tevent_req_error ( req , err ) ;
2012-03-11 13:13:51 +04:00
return ;
}
state - > reply_len = len ;
dump_data ( 10 , state - > reply , state - > reply_len ) ;
tevent_req_done ( req ) ;
}
2018-01-04 23:26:58 +03:00
static int dns_udp_request_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
uint8_t * * reply ,
size_t * reply_len )
2012-03-11 13:13:51 +04:00
{
struct dns_udp_request_state * state = tevent_req_data ( req ,
struct dns_udp_request_state ) ;
2015-12-06 13:31:23 +03:00
int err ;
2012-03-11 13:13:51 +04:00
2015-12-06 13:31:23 +03:00
if ( tevent_req_is_unix_error ( req , & err ) ) {
2012-03-11 13:13:51 +04:00
tevent_req_received ( req ) ;
2015-12-06 13:31:23 +03:00
return err ;
2012-03-11 13:13:51 +04:00
}
* reply = talloc_move ( mem_ctx , & state - > reply ) ;
* reply_len = state - > reply_len ;
tevent_req_received ( req ) ;
2015-12-06 13:31:23 +03:00
return 0 ;
2012-03-11 13:13:51 +04:00
}
2017-12-29 00:35:46 +03:00
struct dns_tcp_request_state {
struct tevent_context * ev ;
struct tstream_context * stream ;
const uint8_t * query ;
size_t query_len ;
uint8_t dns_msglen_hdr [ 2 ] ;
struct iovec iov [ 2 ] ;
size_t nread ;
uint8_t * reply ;
} ;
static void dns_tcp_request_connected ( struct tevent_req * subreq ) ;
static void dns_tcp_request_sent ( struct tevent_req * subreq ) ;
static int dns_tcp_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count ) ;
static void dns_tcp_request_received ( struct tevent_req * subreq ) ;
2018-01-04 23:26:58 +03:00
static struct tevent_req * dns_tcp_request_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const char * server_addr_string ,
const uint8_t * query ,
size_t query_len )
2017-12-29 00:35:46 +03:00
{
struct tevent_req * req , * subreq ;
struct dns_tcp_request_state * state ;
struct tsocket_address * local , * remote ;
int ret ;
req = tevent_req_create ( mem_ctx , & state ,
struct dns_tcp_request_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > query = query ;
state - > query_len = query_len ;
if ( query_len > UINT16_MAX ) {
tevent_req_error ( req , EMSGSIZE ) ;
return tevent_req_post ( req , ev ) ;
}
ret = tsocket_address_inet_from_strings ( state , " ip " , NULL , 0 , & local ) ;
if ( ret ! = 0 ) {
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
}
2021-09-18 01:02:11 +03:00
ret = tsocket_address_inet_from_hostport_strings (
state , " ip " , server_addr_string , DNS_SERVICE_PORT , & remote ) ;
2017-12-29 00:35:46 +03:00
if ( ret ! = 0 ) {
tevent_req_error ( req , errno ) ;
return tevent_req_post ( req , ev ) ;
}
subreq = tstream_inet_tcp_connect_send ( state , state - > ev ,
local , remote ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , dns_tcp_request_connected , req ) ;
return req ;
}
static void dns_tcp_request_connected ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct dns_tcp_request_state * state = tevent_req_data (
req , struct dns_tcp_request_state ) ;
int ret , err ;
ret = tstream_inet_tcp_connect_recv ( subreq , & err , state ,
& state - > stream , NULL ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , err ) ;
return ;
}
RSSVAL ( state - > dns_msglen_hdr , 0 , state - > query_len ) ;
state - > iov [ 0 ] = ( struct iovec ) {
. iov_base = state - > dns_msglen_hdr ,
. iov_len = sizeof ( state - > dns_msglen_hdr )
} ;
state - > iov [ 1 ] = ( struct iovec ) {
. iov_base = discard_const_p ( void , state - > query ) ,
. iov_len = state - > query_len
} ;
subreq = tstream_writev_send ( state , state - > ev , state - > stream ,
state - > iov , ARRAY_SIZE ( state - > iov ) ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , dns_tcp_request_sent , req ) ;
}
static void dns_tcp_request_sent ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct dns_tcp_request_state * state = tevent_req_data (
req , struct dns_tcp_request_state ) ;
int ret , err ;
ret = tstream_writev_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , err ) ;
return ;
}
subreq = tstream_readv_pdu_send ( state , state - > ev , state - > stream ,
dns_tcp_request_next_vector , state ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , dns_tcp_request_received , req ) ;
}
static int dns_tcp_request_next_vector ( struct tstream_context * stream ,
void * private_data ,
TALLOC_CTX * mem_ctx ,
struct iovec * * _vector ,
size_t * _count )
{
struct dns_tcp_request_state * state = talloc_get_type_abort (
private_data , struct dns_tcp_request_state ) ;
struct iovec * vector ;
uint16_t msglen ;
if ( state - > nread = = 0 ) {
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
vector [ 0 ] = ( struct iovec ) {
. iov_base = state - > dns_msglen_hdr ,
. iov_len = sizeof ( state - > dns_msglen_hdr )
} ;
state - > nread = sizeof ( state - > dns_msglen_hdr ) ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
if ( state - > nread = = sizeof ( state - > dns_msglen_hdr ) ) {
msglen = RSVAL ( state - > dns_msglen_hdr , 0 ) ;
state - > reply = talloc_array ( state , uint8_t , msglen ) ;
if ( state - > reply = = NULL ) {
return - 1 ;
}
vector = talloc_array ( mem_ctx , struct iovec , 1 ) ;
if ( vector = = NULL ) {
return - 1 ;
}
vector [ 0 ] = ( struct iovec ) {
. iov_base = state - > reply ,
. iov_len = msglen
} ;
state - > nread + = msglen ;
* _vector = vector ;
* _count = 1 ;
return 0 ;
}
* _vector = NULL ;
* _count = 0 ;
return 0 ;
}
static void dns_tcp_request_received ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
int ret , err ;
ret = tstream_readv_pdu_recv ( subreq , & err ) ;
TALLOC_FREE ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , err ) ;
return ;
}
tevent_req_done ( req ) ;
}
2018-01-04 23:26:58 +03:00
static int dns_tcp_request_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
uint8_t * * reply ,
size_t * reply_len )
2017-12-29 00:35:46 +03:00
{
struct dns_tcp_request_state * state = tevent_req_data (
req , struct dns_tcp_request_state ) ;
int err ;
if ( tevent_req_is_unix_error ( req , & err ) ) {
tevent_req_received ( req ) ;
return err ;
}
* reply_len = talloc_array_length ( state - > reply ) ;
* reply = talloc_move ( mem_ctx , & state - > reply ) ;
tevent_req_received ( req ) ;
return 0 ;
}
2017-12-29 13:01:29 +03:00
struct dns_cli_request_state {
struct tevent_context * ev ;
const char * nameserver ;
uint16_t req_id ;
DATA_BLOB query ;
struct dns_name_packet * reply ;
} ;
static void dns_cli_request_udp_done ( struct tevent_req * subreq ) ;
static void dns_cli_request_tcp_done ( struct tevent_req * subreq ) ;
struct tevent_req * dns_cli_request_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
const char * nameserver ,
const char * name ,
enum dns_qclass qclass ,
enum dns_qtype qtype )
{
struct tevent_req * req , * subreq ;
struct dns_cli_request_state * state ;
struct dns_name_question question ;
struct dns_name_packet out_packet ;
enum ndr_err_code ndr_err ;
req = tevent_req_create ( mem_ctx , & state ,
struct dns_cli_request_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > nameserver = nameserver ;
DBG_DEBUG ( " Asking %s for %s/%d/%d via UDP \n " , nameserver ,
name , ( int ) qclass , ( int ) qtype ) ;
generate_random_buffer ( ( uint8_t * ) & state - > req_id ,
sizeof ( state - > req_id ) ) ;
question = ( struct dns_name_question ) {
. name = discard_const_p ( char , name ) ,
. question_type = qtype , . question_class = qclass
} ;
out_packet = ( struct dns_name_packet ) {
. id = state - > req_id ,
. operation = DNS_OPCODE_QUERY | DNS_FLAG_RECURSION_DESIRED ,
. qdcount = 1 ,
. questions = & question
} ;
ndr_err = ndr_push_struct_blob (
& state - > query , state , & out_packet ,
( ndr_push_flags_fn_t ) ndr_push_dns_name_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
tevent_req_error ( req , ndr_map_error2errno ( ndr_err ) ) ;
return tevent_req_post ( req , ev ) ;
}
subreq = dns_udp_request_send ( state , state - > ev , state - > nameserver ,
state - > query . data , state - > query . length ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , dns_cli_request_udp_done , req ) ;
return req ;
}
static void dns_cli_request_udp_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct dns_cli_request_state * state = tevent_req_data (
req , struct dns_cli_request_state ) ;
DATA_BLOB reply ;
enum ndr_err_code ndr_err ;
2022-01-20 14:23:43 +03:00
uint16_t reply_id , operation ;
2017-12-29 13:01:29 +03:00
int ret ;
ret = dns_udp_request_recv ( subreq , state , & reply . data , & reply . length ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_error ( req , ret ) ) {
return ;
}
2022-01-20 14:23:43 +03:00
if ( reply . length < 4 ) {
DBG_DEBUG ( " Short DNS packet: length=%zu \n " , reply . length ) ;
tevent_req_error ( req , EINVAL ) ;
2017-12-29 13:01:29 +03:00
return ;
}
2022-01-20 14:23:43 +03:00
reply_id = PULL_BE_U16 ( reply . data , 0 ) ;
if ( reply_id ! = state - > req_id ) {
2017-12-29 13:01:29 +03:00
DBG_DEBUG ( " Got id % " PRIu16 " , expected % " PRIu16 " \n " ,
state - > reply - > id , state - > req_id ) ;
tevent_req_error ( req , ENOMSG ) ;
return ;
}
2022-01-20 14:23:43 +03:00
operation = PULL_BE_U16 ( reply . data , 2 ) ;
if ( ( operation & DNS_FLAG_TRUNCATION ) ! = 0 ) {
DBG_DEBUG ( " Reply was truncated, retrying TCP \n " ) ;
subreq = dns_tcp_request_send (
state ,
state - > ev ,
state - > nameserver ,
state - > query . data ,
state - > query . length ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , dns_cli_request_tcp_done , req ) ;
2017-12-29 13:01:29 +03:00
return ;
}
2022-01-20 14:23:43 +03:00
state - > reply = talloc ( state , struct dns_name_packet ) ;
if ( tevent_req_nomem ( state - > reply , req ) ) {
return ;
}
2017-12-29 13:01:29 +03:00
2022-01-20 14:23:43 +03:00
ndr_err = ndr_pull_struct_blob (
& reply , state - > reply , state - > reply ,
( ndr_pull_flags_fn_t ) ndr_pull_dns_name_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
tevent_req_error ( req , ndr_map_error2errno ( ndr_err ) ) ;
2017-12-29 13:01:29 +03:00
return ;
}
2022-01-20 14:23:43 +03:00
TALLOC_FREE ( reply . data ) ;
tevent_req_done ( req ) ;
2017-12-29 13:01:29 +03:00
}
static void dns_cli_request_tcp_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct dns_cli_request_state * state = tevent_req_data (
req , struct dns_cli_request_state ) ;
DATA_BLOB reply ;
enum ndr_err_code ndr_err ;
int ret ;
ret = dns_tcp_request_recv ( subreq , state , & reply . data , & reply . length ) ;
TALLOC_FREE ( subreq ) ;
if ( tevent_req_error ( req , ret ) ) {
return ;
}
state - > reply = talloc ( state , struct dns_name_packet ) ;
if ( tevent_req_nomem ( state - > reply , req ) ) {
return ;
}
ndr_err = ndr_pull_struct_blob (
& reply , state - > reply , state - > reply ,
( ndr_pull_flags_fn_t ) ndr_pull_dns_name_packet ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
tevent_req_error ( req , ndr_map_error2errno ( ndr_err ) ) ;
return ;
}
TALLOC_FREE ( reply . data ) ;
if ( state - > reply - > id ! = state - > req_id ) {
DBG_DEBUG ( " Got id % " PRIu16 " , expected % " PRIu16 " \n " ,
state - > reply - > id , state - > req_id ) ;
tevent_req_error ( req , ENOMSG ) ;
return ;
}
DBG_DEBUG ( " Got op=%x % " PRIu16 " /% " PRIu16 " /% " PRIu16 " /% " PRIu16
" recs \n " , ( int ) state - > reply - > operation ,
state - > reply - > qdcount , state - > reply - > ancount ,
state - > reply - > nscount , state - > reply - > nscount ) ;
tevent_req_done ( req ) ;
}
int dns_cli_request_recv ( struct tevent_req * req , TALLOC_CTX * mem_ctx ,
struct dns_name_packet * * reply )
{
struct dns_cli_request_state * state = tevent_req_data (
req , struct dns_cli_request_state ) ;
int err ;
if ( tevent_req_is_unix_error ( req , & err ) ) {
return err ;
}
* reply = talloc_move ( mem_ctx , & state - > reply ) ;
return 0 ;
}