2006-11-18 00:46:26 +03:00
/*
Linux DNS client library implementation
Copyright ( C ) 2006 Gerald Carter < jerry @ samba . org >
* * NOTE ! The following LGPL license applies to the libaddns
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
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
2007-07-10 07:52:17 +04:00
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
2006-11-18 00:46:26 +03:00
*/
# include "dns.h"
# include "assert.h"
struct dns_buffer * dns_create_buffer ( TALLOC_CTX * mem_ctx )
{
struct dns_buffer * result ;
if ( ! ( result = talloc ( mem_ctx , struct dns_buffer ) ) ) {
return NULL ;
}
result - > offset = 0 ;
result - > error = ERROR_DNS_SUCCESS ;
/*
* Small inital size to excercise the realloc code
*/
result - > size = 2 ;
if ( ! ( result - > data = TALLOC_ARRAY ( result , uint8 , result - > size ) ) ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
return result ;
}
void dns_marshall_buffer ( struct dns_buffer * buf , const uint8 * data ,
size_t len )
{
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
if ( buf - > offset + len < buf - > offset ) {
/*
* Wraparound !
*/
buf - > error = ERROR_DNS_INVALID_PARAMETER ;
return ;
}
if ( ( buf - > offset + len ) > 0xffff ) {
/*
* Only 64 k possible
*/
buf - > error = ERROR_DNS_INVALID_PARAMETER ;
return ;
}
if ( buf - > offset + len > buf - > size ) {
size_t new_size = buf - > offset + len ;
uint8 * new_data ;
/*
* Don ' t do too many reallocs , round up to some multiple
*/
new_size + = ( 64 - ( new_size % 64 ) ) ;
if ( ! ( new_data = TALLOC_REALLOC_ARRAY ( buf , buf - > data , uint8 ,
new_size ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
return ;
}
buf - > size = new_size ;
buf - > data = new_data ;
}
memcpy ( buf - > data + buf - > offset , data , len ) ;
buf - > offset + = len ;
return ;
}
void dns_marshall_uint16 ( struct dns_buffer * buf , uint16 val )
{
uint16 n_val = htons ( val ) ;
dns_marshall_buffer ( buf , ( uint8 * ) & n_val , sizeof ( n_val ) ) ;
}
void dns_marshall_uint32 ( struct dns_buffer * buf , uint32 val )
{
uint32 n_val = htonl ( val ) ;
dns_marshall_buffer ( buf , ( uint8 * ) & n_val , sizeof ( n_val ) ) ;
}
void dns_unmarshall_buffer ( struct dns_buffer * buf , uint8 * data ,
size_t len )
{
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
if ( ( len > buf - > size ) | | ( buf - > offset + len > buf - > size ) ) {
buf - > error = ERROR_DNS_INVALID_MESSAGE ;
return ;
}
memcpy ( ( void * ) data , ( const void * ) ( buf - > data + buf - > offset ) , len ) ;
buf - > offset + = len ;
return ;
}
void dns_unmarshall_uint16 ( struct dns_buffer * buf , uint16 * val )
{
uint16 n_val ;
dns_unmarshall_buffer ( buf , ( uint8 * ) & n_val , sizeof ( n_val ) ) ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
* val = ntohs ( n_val ) ;
}
void dns_unmarshall_uint32 ( struct dns_buffer * buf , uint32 * val )
{
uint32 n_val ;
dns_unmarshall_buffer ( buf , ( uint8 * ) & n_val , sizeof ( n_val ) ) ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
* val = ntohl ( n_val ) ;
}
void dns_marshall_domain_name ( struct dns_buffer * buf ,
const struct dns_domain_name * name )
{
struct dns_domain_label * label ;
char end_char = ' \0 ' ;
/*
* TODO : Implement DNS compression
*/
for ( label = name - > pLabelList ; label ! = NULL ; label = label - > next ) {
uint8 len = label - > len ;
dns_marshall_buffer ( buf , ( uint8 * ) & len , sizeof ( len ) ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
dns_marshall_buffer ( buf , ( uint8 * ) label - > label , len ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
}
dns_marshall_buffer ( buf , ( uint8 * ) & end_char , 1 ) ;
}
static void dns_unmarshall_label ( TALLOC_CTX * mem_ctx ,
int level ,
struct dns_buffer * buf ,
struct dns_domain_label * * plabel )
{
struct dns_domain_label * label ;
uint8 len ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
if ( level > 128 ) {
/*
* Protect against recursion
*/
buf - > error = ERROR_DNS_INVALID_MESSAGE ;
return ;
}
dns_unmarshall_buffer ( buf , & len , sizeof ( len ) ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
if ( len = = 0 ) {
* plabel = NULL ;
return ;
}
if ( ( len & 0xc0 ) = = 0xc0 ) {
/*
* We ' ve got a compressed name . Build up a new " fake " buffer
* and using the calculated offset .
*/
struct dns_buffer new_buf ;
uint8 low ;
dns_unmarshall_buffer ( buf , & low , sizeof ( low ) ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
new_buf = * buf ;
new_buf . offset = len & 0x3f ;
new_buf . offset < < = 8 ;
new_buf . offset | = low ;
dns_unmarshall_label ( mem_ctx , level + 1 , & new_buf , plabel ) ;
buf - > error = new_buf . error ;
return ;
}
if ( ( len & 0xc0 ) ! = 0 ) {
buf - > error = ERROR_DNS_INVALID_NAME ;
return ;
}
if ( ! ( label = talloc ( mem_ctx , struct dns_domain_label ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
return ;
}
label - > len = len ;
if ( ! ( label - > label = TALLOC_ARRAY ( label , char , len + 1 ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
goto error ;
}
dns_unmarshall_buffer ( buf , ( uint8 * ) label - > label , len ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) goto error ;
dns_unmarshall_label ( label , level + 1 , buf , & label - > next ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) goto error ;
* plabel = label ;
return ;
error :
TALLOC_FREE ( label ) ;
return ;
}
void dns_unmarshall_domain_name ( TALLOC_CTX * mem_ctx ,
struct dns_buffer * buf ,
struct dns_domain_name * * pname )
{
struct dns_domain_name * name ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) return ;
if ( ! ( name = talloc ( mem_ctx , struct dns_domain_name ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
2008-10-05 00:07:14 +04:00
return ;
2006-11-18 00:46:26 +03:00
}
dns_unmarshall_label ( name , 0 , buf , & name - > pLabelList ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) {
return ;
}
* pname = name ;
return ;
}
static void dns_marshall_question ( struct dns_buffer * buf ,
const struct dns_question * q )
{
dns_marshall_domain_name ( buf , q - > name ) ;
dns_marshall_uint16 ( buf , q - > q_type ) ;
dns_marshall_uint16 ( buf , q - > q_class ) ;
}
static void dns_unmarshall_question ( TALLOC_CTX * mem_ctx ,
struct dns_buffer * buf ,
struct dns_question * * pq )
{
struct dns_question * q ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
if ( ! ( q = talloc ( mem_ctx , struct dns_question ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
return ;
}
dns_unmarshall_domain_name ( q , buf , & q - > name ) ;
dns_unmarshall_uint16 ( buf , & q - > q_type ) ;
dns_unmarshall_uint16 ( buf , & q - > q_class ) ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
* pq = q ;
}
static void dns_marshall_rr ( struct dns_buffer * buf ,
const struct dns_rrec * r )
{
dns_marshall_domain_name ( buf , r - > name ) ;
dns_marshall_uint16 ( buf , r - > type ) ;
dns_marshall_uint16 ( buf , r - > r_class ) ;
dns_marshall_uint32 ( buf , r - > ttl ) ;
dns_marshall_uint16 ( buf , r - > data_length ) ;
dns_marshall_buffer ( buf , r - > data , r - > data_length ) ;
}
static void dns_unmarshall_rr ( TALLOC_CTX * mem_ctx ,
struct dns_buffer * buf ,
struct dns_rrec * * pr )
{
struct dns_rrec * r ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
if ( ! ( r = talloc ( mem_ctx , struct dns_rrec ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
return ;
}
dns_unmarshall_domain_name ( r , buf , & r - > name ) ;
dns_unmarshall_uint16 ( buf , & r - > type ) ;
dns_unmarshall_uint16 ( buf , & r - > r_class ) ;
dns_unmarshall_uint32 ( buf , & r - > ttl ) ;
dns_unmarshall_uint16 ( buf , & r - > data_length ) ;
r - > data = NULL ;
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
if ( r - > data_length ! = 0 ) {
if ( ! ( r - > data = TALLOC_ARRAY ( r , uint8 , r - > data_length ) ) ) {
buf - > error = ERROR_DNS_NO_MEMORY ;
return ;
}
dns_unmarshall_buffer ( buf , r - > data , r - > data_length ) ;
}
if ( ! ( ERR_DNS_IS_OK ( buf - > error ) ) ) return ;
* pr = r ;
}
DNS_ERROR dns_marshall_request ( TALLOC_CTX * mem_ctx ,
const struct dns_request * req ,
struct dns_buffer * * pbuf )
{
struct dns_buffer * buf ;
uint16 i ;
if ( ! ( buf = dns_create_buffer ( mem_ctx ) ) ) {
return ERROR_DNS_NO_MEMORY ;
}
dns_marshall_uint16 ( buf , req - > id ) ;
dns_marshall_uint16 ( buf , req - > flags ) ;
dns_marshall_uint16 ( buf , req - > num_questions ) ;
dns_marshall_uint16 ( buf , req - > num_answers ) ;
dns_marshall_uint16 ( buf , req - > num_auths ) ;
dns_marshall_uint16 ( buf , req - > num_additionals ) ;
for ( i = 0 ; i < req - > num_questions ; i + + ) {
dns_marshall_question ( buf , req - > questions [ i ] ) ;
}
for ( i = 0 ; i < req - > num_answers ; i + + ) {
dns_marshall_rr ( buf , req - > answers [ i ] ) ;
}
for ( i = 0 ; i < req - > num_auths ; i + + ) {
dns_marshall_rr ( buf , req - > auths [ i ] ) ;
}
for ( i = 0 ; i < req - > num_additionals ; i + + ) {
dns_marshall_rr ( buf , req - > additionals [ i ] ) ;
}
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) {
DNS_ERROR err = buf - > error ;
TALLOC_FREE ( buf ) ;
return err ;
}
* pbuf = buf ;
return ERROR_DNS_SUCCESS ;
}
DNS_ERROR dns_unmarshall_request ( TALLOC_CTX * mem_ctx ,
struct dns_buffer * buf ,
struct dns_request * * preq )
{
struct dns_request * req ;
uint16 i ;
DNS_ERROR err ;
if ( ! ( req = TALLOC_ZERO_P ( mem_ctx , struct dns_request ) ) ) {
return ERROR_DNS_NO_MEMORY ;
}
dns_unmarshall_uint16 ( buf , & req - > id ) ;
dns_unmarshall_uint16 ( buf , & req - > flags ) ;
dns_unmarshall_uint16 ( buf , & req - > num_questions ) ;
dns_unmarshall_uint16 ( buf , & req - > num_answers ) ;
dns_unmarshall_uint16 ( buf , & req - > num_auths ) ;
dns_unmarshall_uint16 ( buf , & req - > num_additionals ) ;
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) goto error ;
err = ERROR_DNS_NO_MEMORY ;
if ( ( req - > num_questions ! = 0 ) & &
! ( req - > questions = TALLOC_ARRAY ( req , struct dns_question * ,
req - > num_questions ) ) ) {
goto error ;
}
if ( ( req - > num_answers ! = 0 ) & &
! ( req - > answers = TALLOC_ARRAY ( req , struct dns_rrec * ,
req - > num_answers ) ) ) {
goto error ;
}
if ( ( req - > num_auths ! = 0 ) & &
! ( req - > auths = TALLOC_ARRAY ( req , struct dns_rrec * ,
req - > num_auths ) ) ) {
goto error ;
}
if ( ( req - > num_additionals ! = 0 ) & &
! ( req - > additionals = TALLOC_ARRAY ( req , struct dns_rrec * ,
req - > num_additionals ) ) ) {
goto error ;
}
for ( i = 0 ; i < req - > num_questions ; i + + ) {
dns_unmarshall_question ( req - > questions , buf ,
& req - > questions [ i ] ) ;
}
for ( i = 0 ; i < req - > num_answers ; i + + ) {
dns_unmarshall_rr ( req - > answers , buf ,
& req - > answers [ i ] ) ;
}
for ( i = 0 ; i < req - > num_auths ; i + + ) {
dns_unmarshall_rr ( req - > auths , buf ,
& req - > auths [ i ] ) ;
}
for ( i = 0 ; i < req - > num_additionals ; i + + ) {
dns_unmarshall_rr ( req - > additionals , buf ,
& req - > additionals [ i ] ) ;
}
if ( ! ERR_DNS_IS_OK ( buf - > error ) ) {
err = buf - > error ;
goto error ;
}
* preq = req ;
return ERROR_DNS_SUCCESS ;
error :
err = buf - > error ;
TALLOC_FREE ( req ) ;
return err ;
}
struct dns_request * dns_update2request ( struct dns_update_request * update )
{
struct dns_request * req ;
/*
* This is a non - specified construct that happens to work on Linux / gcc
* and I would expect it to work everywhere else . dns_request and
* dns_update_request are essentially the same structures with
* different names , so any difference would mean that the compiler
* applied two different variations of padding given the same types in
* the structures .
*/
req = ( struct dns_request * ) ( void * ) update ;
/*
* The assert statement here looks like we could do the equivalent
* assignments to get portable , but it would mean that we have to
* allocate the dns_question record for the dns_zone records . We
* assume that if this assert works then the same holds true for
* dns_zone < > dns_question as well .
*/
# ifdef DEVELOPER
assert ( ( req - > id = = update - > id ) & & ( req - > flags = = update - > flags ) & &
( req - > num_questions = = update - > num_zones ) & &
( req - > num_answers = = update - > num_preqs ) & &
( req - > num_auths = = update - > num_updates ) & &
( req - > num_additionals = = update - > num_additionals ) & &
( req - > questions = =
( struct dns_question * * ) ( void * ) update - > zones ) & &
( req - > answers = = update - > preqs ) & &
( req - > auths = = update - > updates ) & &
( req - > additionals = = update - > additionals ) ) ;
# endif
return req ;
}
struct dns_update_request * dns_request2update ( struct dns_request * request )
{
/*
* For portability concerns see dns_update2request ;
*/
return ( struct dns_update_request * ) ( void * ) request ;
}
DNS_ERROR dns_marshall_update_request ( TALLOC_CTX * mem_ctx ,
struct dns_update_request * update ,
struct dns_buffer * * pbuf )
{
return dns_marshall_request ( mem_ctx , dns_update2request ( update ) , pbuf ) ;
}
DNS_ERROR dns_unmarshall_update_request ( TALLOC_CTX * mem_ctx ,
struct dns_buffer * buf ,
struct dns_update_request * * pupreq )
{
/*
* See comments above about portability . If the above works , this will
* as well .
*/
return dns_unmarshall_request ( mem_ctx , buf ,
( struct dns_request * * ) ( void * ) pupreq ) ;
}
uint16 dns_response_code ( uint16 flags )
{
return flags & 0xF ;
}